dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #36542
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 18687: tracker-capture: more work in centralizing registration, enrollment and profile editing
------------------------------------------------------------
revno: 18687
committer: Abyot Asalefew Gizaw <abyota@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2015-03-26 13:26:37 +0100
message:
tracker-capture: more work in centralizing registration, enrollment and profile editing
removed:
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/default-registration-form.html
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/enrollment-dates-form.html
added:
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/enrollment-dates-form.html
modified:
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/enrollment/enrollment-controller.js
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/profile/profile-controller.js
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js
dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration.html
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/enrollment/enrollment-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/enrollment/enrollment-controller.js 2015-03-25 16:43:47 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/enrollment/enrollment-controller.js 2015-03-26 12:26:37 +0000
@@ -7,10 +7,7 @@
$timeout,
DateUtils,
storage,
- AttributesFactory,
CurrentSelection,
- TEFormService,
- CustomFormService,
EnrollmentService,
ModalService) {
@@ -77,35 +74,13 @@
}
});
- $scope.loadEnrollmentDetails = function(enrollment) {
-
- $scope.attributesForEnrollment = [];
- $scope.attributes = [];
+ $scope.loadEnrollmentDetails = function(enrollment){
$scope.showEnrollmentHistoryDiv = false;
$scope.selectedEnrollment = enrollment;
- AttributesFactory.getByProgram($scope.selectedProgram).then(function(atts){
- $scope.attributes = atts;
- $scope.customFormExists = false;
-
- if($scope.selectedProgram && $scope.selectedProgram.id){
- TEFormService.getByProgram($scope.selectedProgram, atts).then(function(teForm){
- if(angular.isObject(teForm)){
- $scope.customFormExists = true;
- $scope.trackedEntityForm = teForm;
- $scope.customForm = CustomFormService.getForTrackedEntity($scope.trackedEntityForm, 'ENROLLMENT');
- }
- if($scope.selectedEnrollment.enrollment){
- $scope.broadCastSelections('dashboardWidgets');
- }
- });
- }
- else{
- if($scope.selectedEnrollment.enrollment){
- $scope.broadCastSelections('dashboardWidgets');
- }
- }
- });
+ if($scope.selectedEnrollment.enrollment){
+ $scope.broadCastSelections('dashboardWidgets');
+ }
};
$scope.showNewEnrollment = function(){
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/profile/profile-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/profile/profile-controller.js 2015-03-25 16:43:47 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/profile/profile-controller.js 2015-03-26 12:26:37 +0000
@@ -4,10 +4,7 @@
function($rootScope,
$scope,
$timeout,
- CurrentSelection,
- CustomFormService,
- TEFormService,
- AttributesFactory) {
+ CurrentSelection) {
$scope.editingDisabled = true;
$scope.enrollmentEditing = false;
@@ -16,6 +13,21 @@
//listen for the selected entity
var selections = {};
$scope.$on('dashboardWidgets', function(event, args) {
+ listenToBroadCast();
+ });
+
+ //listen to changes in profile
+ $scope.$on('profileWidget', function(event, args){
+ listenToBroadCast();
+ });
+
+ //listen to changes in enrollment editing
+ $scope.$on('enrollmentEditing', function(event, args){
+ $scope.enrollmentEditing = args.enrollmentEditing;
+ });
+
+ var listenToBroadCast = function(){
+ $scope.editingDisabled = true;
selections = CurrentSelection.get();
$scope.selectedTei = angular.copy(selections.tei);
$scope.trackedEntity = selections.te;
@@ -34,29 +46,11 @@
});
delete $scope.selectedTei.attributes;
-
- AttributesFactory.getByProgram($scope.selectedProgram).then(function(atts){
- $scope.attributes = atts;
- $scope.customFormExists = false;
- if($scope.selectedProgram && $scope.selectedProgram.id){
- TEFormService.getByProgram($scope.selectedProgram, atts).then(function(teForm){
- if(angular.isObject(teForm)){
- $scope.customFormExists = true;
- $scope.trackedEntityForm = teForm;
- $scope.customForm = CustomFormService.getForTrackedEntity($scope.trackedEntityForm, 'PROFILE');
- }
- });
- }
- });
+
$timeout(function() {
$rootScope.$broadcast('registrationWidget', {registrationMode: 'PROFILE', selectedTei: $scope.selectedTei, enrollment: $scope.selectedEnrollment});
}, 100);
- });
-
- //listen for enrollment editing
- $scope.$on('enrollmentEditing', function(event, args){
- $scope.enrollmentEditing = args.enrollmentEditing;
- });
+ };
$scope.enableEdit = function(){
$scope.teiOriginal = angular.copy($scope.selectedTei);
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html 2015-03-26 12:26:37 +0000
@@ -0,0 +1,78 @@
+<h3 ng-if="widget !== 'PROFILE'">{{'profile'| translate}}</h3>
+<table class="table-borderless table-striped">
+ <tr ng-repeat="attribute in attributes">
+ <td>
+ {{attribute.name}}<span ng-if="attribute.mandatory" class="required">*</span>
+ </td>
+ <td>
+ <ng-form name="innerForm">
+ <span ng-switch="attribute.valueType">
+ <span ng-switch-when="date">
+ <input type="text"
+ placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
+ name="foo"
+ class="form-control"
+ d2-date
+ max-date="attribute.allowFutureDate ? '' : 0"
+ ng-model="selectedTei[attribute.id]"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory"/>
+ </span>
+ <span ng-switch-when="trueOnly">
+ <input type="checkbox"
+ name="foo"
+ class="form-control"
+ ng-model="selectedTei[attribute.id]"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory"/>
+ </span>
+ <span ng-switch-when="bool">
+ <select name="foo"
+ ng-model="selectedTei[attribute.id]"
+ class="form-control"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory">
+ <option value="">{{'please_select'| translate}}</option>
+ <option value="false">{{'no'| translate}}</option>
+ <option value="true">{{'yes'| translate}}</option>
+ </select>
+ </span>
+ <span ng-switch-when="optionSet">
+ <input type="text"
+ name="foo"
+ class="form-control"
+ ng-model="selectedTei[attribute.id]"
+ typeahead="option.name as option.name for option in optionSets[attributesById[attribute.id].optionSet.id].options | filter:$viewValue | limitTo:20"
+ typeahead-open-on-focus
+ ng-disabled="editingDisabled" ng-required="attribute.mandatory"/>
+ </span>
+ <span ng-switch-when="number">
+ <input type="text"
+ name="foo"
+ class="form-control"
+ ng-model="selectedTei[attribute.id]"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory"/>
+ </span>
+ <span ng-switch-when="email">
+ <input type="email"
+ name="foo"
+ class="form-control"
+ ng-model="selectedTei[attribute.id]"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory"/>
+ </span>
+ <span ng-switch-default>
+ <input type="text"
+ name="foo"
+ class="form-control"
+ ng-model="selectedTei[attribute.id]"
+ ng-disabled="editingDisabled"
+ ng-required="attribute.mandatory"/>
+ </span>
+ </span>
+ </ng-form>
+ <span ng-show="outerForm.submitted && innerForm.foo.$invalid" class="error">{{'required'| translate}}</span>
+ </td>
+ </tr>
+</table>
\ No newline at end of file
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/enrollment-dates-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/enrollment-dates-form.html 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/enrollment-dates-form.html 2015-03-26 12:26:37 +0000
@@ -0,0 +1,35 @@
+<table class="dhis2-list-table-striped dhis2-table-hover">
+ <tr>
+ <td>
+ {{selectedProgram.dateOfEnrollmentDescription}}
+ </td>
+ <td>
+ <input type="text"
+ placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
+ name="dateOfEnrollment"
+ class="form-control"
+ d2-date
+ ng-model="selectedEnrollment.dateOfEnrollment"
+ max-date="selectedProgram.selectEnrollmentDatesInFuture ? '' : 0"
+ min-date=""
+ ng-required="true"/>
+ <span ng-show="outerForm.submitted && outerForm.dateOfEnrollment.$invalid" class="error">{{'required'| translate}}</span>
+ </td>
+ </tr>
+ <tr ng-if="selectedProgram.displayIncidentDate">
+ <td>
+ {{selectedProgram.dateOfIncidentDescription}}
+ </td>
+ <td>
+ <input type="text"
+ placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
+ name="dateOfIncident"
+ class="form-control"
+ d2-date
+ max-date="selectedProgram.selectIncidentDatesInFuture ? '' : 0"
+ min-date=""
+ ng-model="selectedEnrollment.dateOfIncident"/>
+ </td>
+ </tr>
+</table>
+<hr>
\ No newline at end of file
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js 2015-03-25 16:43:47 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js 2015-03-26 12:26:37 +0000
@@ -101,7 +101,7 @@
};
$scope.registerEntity = function(destination){
-
+
//check for form validity
$scope.outerForm.submitted = true;
if( $scope.outerForm.$invalid ){
@@ -129,43 +129,48 @@
}
RegistrationService.registerOrUpdate($scope.tei, $scope.optionSets, $scope.attributesById).then(function(response){
+
if(response.status === 'SUCCESS'){
- if( $scope.tei && !$scope.tei.trackedEntityInstance ){
- $scope.tei.trackedEntityInstance = response.reference;
+
+ $scope.tei.trackedEntityInstance = response.reference;
+
+ if( $scope.registrationMode === 'PROFILE' ){
+ reloadProfileWidget();
}
-
- if($scope.selectedProgram && $scope.registrationMode !== 'PROFILE'){
- //enroll TEI
- var enrollment = {};
- enrollment.trackedEntityInstance = $scope.tei.trackedEntityInstance;
- enrollment.program = $scope.selectedProgram.id;
- enrollment.status = 'ACTIVE';
- enrollment.dateOfEnrollment = $scope.selectedEnrollment.dateOfEnrollment;
- enrollment.dateOfIncident = $scope.selectedEnrollment.dateOfIncident === '' ? $scope.selectedEnrollment.dateOfEnrollment : $scope.selectedEnrollment.dateOfIncident;
-
- EnrollmentService.enroll(enrollment).then(function(data){
- if(data.status !== 'SUCCESS'){
- //enrollment has failed
- var dialogOptions = {
- headerText: 'enrollment_error',
- bodyText: data.description
- };
- DialogService.showDialog({}, dialogOptions);
- return;
- }
- else{
- enrollment.enrollment = data.reference;
- $scope.selectedEnrollment = enrollment;
- var dhis2Events = EventUtils.autoGenerateEvents($scope.tei.trackedEntityInstance, $scope.selectedProgram, $scope.selectedOrgUnit, enrollment);
- if(dhis2Events.events.length > 0){
- DHIS2EventFactory.create(dhis2Events).then(function(data){
+ else{
+ if( $scope.selectedProgram ){
+ //enroll TEI
+ var enrollment = {};
+ enrollment.trackedEntityInstance = $scope.tei.trackedEntityInstance;
+ enrollment.program = $scope.selectedProgram.id;
+ enrollment.status = 'ACTIVE';
+ enrollment.dateOfEnrollment = $scope.selectedEnrollment.dateOfEnrollment;
+ enrollment.dateOfIncident = $scope.selectedEnrollment.dateOfIncident === '' ? $scope.selectedEnrollment.dateOfEnrollment : $scope.selectedEnrollment.dateOfIncident;
+
+ EnrollmentService.enroll(enrollment).then(function(data){
+ if(data.status !== 'SUCCESS'){
+ //enrollment has failed
+ var dialogOptions = {
+ headerText: 'enrollment_error',
+ bodyText: data.description
+ };
+ DialogService.showDialog({}, dialogOptions);
+ return;
+ }
+ else{
+ enrollment.enrollment = data.reference;
+ $scope.selectedEnrollment = enrollment;
+ var dhis2Events = EventUtils.autoGenerateEvents($scope.tei.trackedEntityInstance, $scope.selectedProgram, $scope.selectedOrgUnit, enrollment);
+ if(dhis2Events.events.length > 0){
+ DHIS2EventFactory.create(dhis2Events).then(function(data){
+ notifyRegistrtaionCompletion(destination, $scope.tei.trackedEntityInstance);
+ });
+ }else{
notifyRegistrtaionCompletion(destination, $scope.tei.trackedEntityInstance);
- });
- }else{
- notifyRegistrtaionCompletion(destination, $scope.tei.trackedEntityInstance);
- }
- }
- });
+ }
+ }
+ });
+ }
}
}
else{//update/registration has failed
@@ -208,6 +213,14 @@
}
};
+ var reloadProfileWidget = function(){
+ var selections = CurrentSelection.get();
+ CurrentSelection.set({tei: $scope.selectedTei, te: $scope.selectedTei.trackedEntity, prs: selections.prs, pr: $scope.selectedProgram, prNames: selections.prNames, prStNames: selections.prStNames, enrollments: selections.enrollments, selectedEnrollment: $scope.selectedEnrollment, optionSets: selections.optionSets});
+ $timeout(function() {
+ $rootScope.$broadcast('profileWidget', {});
+ }, 100);
+ };
+
var notifyRegistrtaionCompletion = function(destination, teiId){
goToDashboard( destination ? destination : 'DASHBOARD', teiId );
/*if($scope.registrationMode === 'REGISTRATION'){
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration.html 2015-03-25 16:43:47 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration.html 2015-03-26 12:26:37 +0000
@@ -26,42 +26,9 @@
<!-- enrollment and incidence dates begin -->
<div ng-if="selectedProgram && !customForm.hasProgramDate && registrationMode !== 'PROFILE'">
- <table class="dhis2-list-table-striped dhis2-table-hover">
- <tr>
- <td>
- {{selectedProgram.dateOfEnrollmentDescription}}
- </td>
- <td>
- <input type="text"
- placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
- name="dateOfEnrollment"
- class="form-control"
- d2-date
- ng-model="selectedEnrollment.dateOfEnrollment"
- max-date="selectedProgram.selectEnrollmentDatesInFuture ? '' : 0"
- min-date=""
- ng-required="true"/>
- <span ng-show="outerForm.submitted && outerForm.dateOfEnrollment.$invalid" class="error">{{'required'| translate}}</span>
- </td>
- </tr>
- <tr ng-if="selectedProgram.displayIncidentDate">
- <td>
- {{selectedProgram.dateOfIncidentDescription}}
- </td>
- <td>
- <input type="text"
- placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
- name="dateOfIncident"
- class="form-control"
- d2-date
- max-date="selectedProgram.selectIncidentDatesInFuture ? '' : 0"
- min-date=""
- ng-model="selectedEnrollment.dateOfIncident"/>
- </td>
- </tr>
- </table>
+ <div ng-include="'components/registration/enrollment-dates-form.html'"></div>
</div>
- <!-- enrollment and incidence dates begin -->
+ <!-- enrollment and incidence dates end -->
<div ng-include="'../dhis-web-commons/customform/custom-form.html'"></div>
@@ -70,8 +37,8 @@
<!-- default registration form begins -->
<div class="row col-sm-12 vertical-spacing" ng-if="!customFormExists">
- <div ng-if="selectedProgram && registrationMode !== 'PROFILE'" ng-include="'views/enrollment-dates-form.html'"></div>
- <div ng-include="'views/default-registration-form.html'"></div>
+ <div ng-if="selectedProgram && registrationMode !== 'PROFILE'" ng-include="'components/registration/enrollment-dates-form.html'"></div>
+ <div ng-include="'components/registration/default-registration-form.html'"></div>
</div>
<!-- default registration form ends -->
=== removed file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/default-registration-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/default-registration-form.html 2015-02-04 12:53:26 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/default-registration-form.html 1970-01-01 00:00:00 +0000
@@ -1,78 +0,0 @@
-<h3 ng-if="widget !== 'PROFILE'">{{'profile'| translate}}</h3>
-<table class="table-borderless table-striped">
- <tr ng-repeat="attribute in attributes">
- <td>
- {{attribute.name}}<span ng-if="attribute.mandatory" class="required">*</span>
- </td>
- <td>
- <ng-form name="innerForm">
- <span ng-switch="attribute.valueType">
- <span ng-switch-when="date">
- <input type="text"
- placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
- name="foo"
- class="form-control"
- d2-date
- max-date="attribute.allowFutureDate ? '' : 0"
- ng-model="selectedTei[attribute.id]"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory"/>
- </span>
- <span ng-switch-when="trueOnly">
- <input type="checkbox"
- name="foo"
- class="form-control"
- ng-model="selectedTei[attribute.id]"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory"/>
- </span>
- <span ng-switch-when="bool">
- <select name="foo"
- ng-model="selectedTei[attribute.id]"
- class="form-control"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory">
- <option value="">{{'please_select'| translate}}</option>
- <option value="false">{{'no'| translate}}</option>
- <option value="true">{{'yes'| translate}}</option>
- </select>
- </span>
- <span ng-switch-when="optionSet">
- <input type="text"
- name="foo"
- class="form-control"
- ng-model="selectedTei[attribute.id]"
- typeahead="option.name as option.name for option in optionSets[attributesById[attribute.id].optionSet.id].options | filter:$viewValue | limitTo:20"
- typeahead-open-on-focus
- ng-disabled="editingDisabled" ng-required="attribute.mandatory"/>
- </span>
- <span ng-switch-when="number">
- <input type="text"
- name="foo"
- class="form-control"
- ng-model="selectedTei[attribute.id]"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory"/>
- </span>
- <span ng-switch-when="email">
- <input type="email"
- name="foo"
- class="form-control"
- ng-model="selectedTei[attribute.id]"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory"/>
- </span>
- <span ng-switch-default>
- <input type="text"
- name="foo"
- class="form-control"
- ng-model="selectedTei[attribute.id]"
- ng-disabled="editingDisabled"
- ng-required="attribute.mandatory"/>
- </span>
- </span>
- </ng-form>
- <span ng-show="outerForm.submitted && innerForm.foo.$invalid" class="error">{{'required'| translate}}</span>
- </td>
- </tr>
-</table>
\ No newline at end of file
=== removed file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/enrollment-dates-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/enrollment-dates-form.html 2015-02-04 12:53:26 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/views/enrollment-dates-form.html 1970-01-01 00:00:00 +0000
@@ -1,35 +0,0 @@
-<table class="dhis2-list-table-striped dhis2-table-hover">
- <tr>
- <td>
- {{selectedProgram.dateOfEnrollmentDescription}}
- </td>
- <td>
- <input type="text"
- placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
- name="dateOfEnrollment"
- class="form-control"
- d2-date
- ng-model="selectedEnrollment.dateOfEnrollment"
- max-date="selectedProgram.selectEnrollmentDatesInFuture ? '' : 0"
- min-date=""
- ng-required="true"/>
- <span ng-show="outerForm.submitted && outerForm.dateOfEnrollment.$invalid" class="error">{{'required'| translate}}</span>
- </td>
- </tr>
- <tr ng-if="selectedProgram.displayIncidentDate">
- <td>
- {{selectedProgram.dateOfIncidentDescription}}
- </td>
- <td>
- <input type="text"
- placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
- name="dateOfIncident"
- class="form-control"
- d2-date
- max-date="selectedProgram.selectIncidentDatesInFuture ? '' : 0"
- min-date=""
- ng-model="selectedEnrollment.dateOfIncident"/>
- </td>
- </tr>
-</table>
-<hr>
\ No newline at end of file