From ca8138fd1334aebe4f4f0341c87a683b45a2979a Mon Sep 17 00:00:00 2001 From: Alchez Date: Fri, 8 Jun 2018 14:55:07 +0530 Subject: [PATCH] [Agriculture] Sort crop tasks chronologically and optimize disease task creation (#14367) * Improve max crop period check and sort agriculture tasks chronologically * Reformat crop cycle logic and add field to optimize task creation --- erpnext/agriculture/doctype/crop/crop.py | 22 ++- .../doctype/crop_cycle/crop_cycle.py | 140 ++++++++++-------- .../detected_disease/detected_disease.json | 39 ++++- 3 files changed, 135 insertions(+), 66 deletions(-) diff --git a/erpnext/agriculture/doctype/crop/crop.py b/erpnext/agriculture/doctype/crop/crop.py index 52594f4d5a2..ef02613d3d9 100644 --- a/erpnext/agriculture/doctype/crop/crop.py +++ b/erpnext/agriculture/doctype/crop/crop.py @@ -3,23 +3,31 @@ # For license information, please see license.txt from __future__ import unicode_literals + import frappe from frappe import _ from frappe.model.document import Document -from frappe import _ + class Crop(Document): def validate(self): - max_period = 0 + self.validate_crop_tasks() + + def validate_crop_tasks(self): for task in self.agriculture_task: - # validate start_day is not > end_day if task.start_day > task.end_day: frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name)) - # to calculate the period of the Crop Cycle - if task.end_day > max_period: max_period = task.end_day - if max_period > self.period: self.period = max_period + + # Verify that the crop period is correct + max_crop_period = max([task.end_day for task in self.agriculture_task]) + self.period = max(self.period, max_crop_period) + + # Sort the crop tasks based on start days, + # maintaining the order for same-day tasks + self.agriculture_task.sort(key=lambda task: task.start_day) + @frappe.whitelist() def get_item_details(item_code): item = frappe.get_doc('Item', item_code) - return { "uom": item.stock_uom, "rate": item.valuation_rate } \ No newline at end of file + return {"uom": item.stock_uom, "rate": item.valuation_rate} diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py index 1d9f3242b01..e0907062002 100644 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py +++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py @@ -3,43 +3,48 @@ # For license information, please see license.txt from __future__ import unicode_literals + +import ast + import frappe from frappe import _ from frappe.model.document import Document -import ast +from frappe.utils import add_days + class CropCycle(Document): def validate(self): - if self.is_new(): - crop = frappe.get_doc('Crop', self.crop) - self.create_project(crop.period, crop.agriculture_task) - if not self.crop_spacing_uom: - self.crop_spacing_uom = crop.crop_spacing_uom - if not self.row_spacing_uom: - self.row_spacing_uom = crop.row_spacing_uom - if not self.project: - self.project = self.name - disease = [] - for detected_disease in self.detected_disease: - disease.append(detected_disease.name) - if disease != []: - self.update_disease(disease) - else: - old_disease, new_disease = [], [] - for detected_disease in self.detected_disease: - new_disease.append(detected_disease.name) - for detected_disease in self.get_doc_before_save().get('detected_disease'): - old_disease.append(detected_disease.name) - if list(set(new_disease)-set(old_disease)) != []: - self.update_disease(list(set(new_disease)-set(old_disease))) - frappe.msgprint(_("All tasks for the detected diseases were imported")) + self.set_missing_values() - def update_disease(self, disease_hashes): - new_disease = [] + def after_insert(self): + self.create_crop_cycle_project() + self.create_tasks_for_diseases() + + def on_update(self): + self.create_tasks_for_diseases() + + def set_missing_values(self): + crop = frappe.get_doc('Crop', self.crop) + + if not self.crop_spacing_uom: + self.crop_spacing_uom = crop.crop_spacing_uom + + if not self.row_spacing_uom: + self.row_spacing_uom = crop.row_spacing_uom + + def create_crop_cycle_project(self): + crop = frappe.get_doc('Crop', self.crop) + + self.project = self.create_project(crop.period, crop.agriculture_task) + self.create_task(crop.agriculture_task, self.project, self.start_date) + + def create_tasks_for_diseases(self): for disease in self.detected_disease: - for disease_hash in disease_hashes: - if disease.name == disease_hash: - self.import_disease_tasks(disease.disease, disease.start_date) + if not disease.tasks_created: + self.import_disease_tasks(disease.disease, disease.start_date) + disease.tasks_created = True + + frappe.msgprint(_("Tasks have been created for managing the {0} disease (on row {1})".format(disease.disease, disease.idx))) def import_disease_tasks(self, disease, start_date): disease_doc = frappe.get_doc('Disease', disease) @@ -47,58 +52,77 @@ class CropCycle(Document): def create_project(self, period, crop_tasks): project = frappe.new_doc("Project") - project.project_name = self.title - project.expected_start_date = self.start_date - project.expected_end_date = frappe.utils.data.add_days(self.start_date, period-1) + project.update({ + "project_name": self.title, + "expected_start_date": self.start_date, + "expected_end_date": add_days(self.start_date, period - 1) + }) project.insert() - self.create_task(crop_tasks, project.as_dict.im_self.name, self.start_date) - return project.as_dict.im_self.name + + return project.name def create_task(self, crop_tasks, project_name, start_date): for crop_task in crop_tasks: task = frappe.new_doc("Task") - task.subject = crop_task.get("task_name") - task.priority = crop_task.get("priority") - task.project = project_name - task.exp_start_date = frappe.utils.data.add_days(start_date, crop_task.get("start_day")-1) - task.exp_end_date = frappe.utils.data.add_days(start_date, crop_task.get("end_day")-1) + task.update({ + "subject": crop_task.get("task_name"), + "priority": crop_task.get("priority"), + "project": project_name, + "exp_start_date": add_days(start_date, crop_task.get("start_day") - 1), + "exp_end_date": add_days(start_date, crop_task.get("end_day") - 1) + }) task.insert() def reload_linked_analysis(self): linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis'] required_fields = ['location', 'name', 'collection_datetime'] output = {} + for doctype in linked_doctypes: output[doctype] = frappe.get_all(doctype, fields=required_fields) + output['Land Unit'] = [] + for land in self.linked_land_unit: output['Land Unit'].append(frappe.get_doc('Land Unit', land.land_unit)) - frappe.publish_realtime("List of Linked Docs", output, user=frappe.session.user) + frappe.publish_realtime("List of Linked Docs", + output, user=frappe.session.user) def append_to_child(self, obj_to_append): for doctype in obj_to_append: for doc_name in set(obj_to_append[doctype]): self.append(doctype, {doctype: doc_name}) + self.save() - def get_coordinates(self, doc): - return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates') - def get_geometry_type(self, doc): - return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type') +def get_coordinates(doc): + return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates') - def is_in_land_unit(self, point, vs): - x, y = point - inside = False - j = len(vs)-1 - i = 0 - while i < len(vs): - xi, yi = vs[i] - xj, yj = vs[j] - intersect = ((yi > y) != (yj > y)) and (x < (xj - xi) * (y - yi) / (yj - yi) + xi) - if intersect: - inside = not inside - i = j - j += 1 - return inside \ No newline at end of file + +def get_geometry_type(doc): + return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type') + + +def is_in_land_unit(point, vs): + x, y = point + inside = False + + j = len(vs) - 1 + i = 0 + + while i < len(vs): + xi, yi = vs[i] + xj, yj = vs[j] + + intersect = ((yi > y) != (yj > y)) and ( + x < (xj - xi) * (y - yi) / (yj - yi) + xi) + + if intersect: + inside = not inside + + i = j + j += 1 + + return inside diff --git a/erpnext/agriculture/doctype/detected_disease/detected_disease.json b/erpnext/agriculture/doctype/detected_disease/detected_disease.json index cf44149cb9b..bfed9a755c7 100644 --- a/erpnext/agriculture/doctype/detected_disease/detected_disease.json +++ b/erpnext/agriculture/doctype/detected_disease/detected_disease.json @@ -14,6 +14,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -41,10 +42,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -71,6 +74,40 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "tasks_created", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Tasks Created", + "length": 0, + "no_copy": 1, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -84,7 +121,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-11-26 21:10:14.753511", + "modified": "2018-06-06 02:24:52.131482", "modified_by": "Administrator", "module": "Agriculture", "name": "Detected Disease",