def trees_import( *, auth=Depends(authorization("trees:import")), db: Session = Depends(get_db), name: str, ) -> Any: """ import trees from geofile """ geofile = crud.geo_file.get_by_name(db, name=name) if not geofile: raise HTTPException(status_code=404, detail=f"{name} not found") if not geofile.crs: raise HTTPException(status_code=415, detail="crs not found") if geofile.driver in ["xlsx", "xls", "csv"]: if not geofile.longitude_column: raise HTTPException(status_code=415, detail="longitude_column not found") if not geofile.latitude_column: raise HTTPException(status_code=415, detail="latitude_column not found") if geofile.status == models.GeoFileStatus.IMPORTING: raise HTTPException( status_code=409, detail=f"{geofile.name} has already started an import process", ) import_geofile_task.delay(name) return geofile
def delete_geo_file( organization_id: int, auth=Depends(authorization("geofiles:delete_geo_file")), *, name: str, db: Session = Depends(get_db), ) -> Any: """ Delete one geofile """ geofile = crud.geo_file.get_by_name(db, name=name) if not geofile: raise HTTPException( status_code=404, detail=f"The geofile with name {name} does not exist in the system", ) try: os.remove(geofile.get_filepath()) except OSError: pass crud.geo_file.remove(db, id=geofile.id) return name
def delete_teams( organization_id: int, *, auth=Depends(authorization("organizations:delete_team")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), team_ids_in: List[int], ): """ bulk delete teams and their members """ teams_out = [] for team_id in team_ids_in: organization_in_db = organization.get(db, id=team_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Team not found") try: members_in_db = crud.organization.get_members(db, id=team_id) for member in members_in_db: current_roles = enforcer.get_roles_for_user_in_domain( str(member["id"]), str(team_id)) for current_role in current_roles: enforcer.delete_roles_for_user_in_domain( str(member["id"]), current_role, str(team_id)) except Exception as e: logging.error(e) organization.remove(db, id=team_id) teams_out.append(dict(organization_in_db.as_dict())) return teams_out
def delete( health_assessment_id: int, *, auth=Depends(authorization("health_assessment:delete")), db: Session = Depends(get_db), ): return crud.health_assessment.remove(db, id=health_assessment_id)
def remove_team( organization_id: int, team_id: int, *, auth=Depends(authorization("organizations:delete_team")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """ delete one team and its members by id """ organization_in_db = organization.get(db, id=team_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Team not found") try: members_in_db = crud.organization.get_members(db, id=team_id) for member in members_in_db: current_roles = enforcer.get_roles_for_user_in_domain( str(member["id"]), str(team_id)) for current_role in current_roles: enforcer.delete_roles_for_user_in_domain( str(member["id"]), current_role, str(team_id)) except Exception as e: logging.error(e) return False organization.remove(db, id=team_id) return True
def delete( id: int, *, auth=Depends(authorization("interventions:delete")), db: Session = Depends(get_db), ): return crud.intervention.remove(db, id=id)
def get( intervention_id: int, *, auth=Depends(authorization("interventions:get")), db: Session = Depends(get_db), ): return crud.intervention.get(db, id=intervention_id)
def get_center_from_organization( *, auth=Depends( authorization("organization:get_center_from_organization")), db: Session = Depends(get_db), organization_id: int, current_user: User = Depends(get_current_user), ) -> Any: """ find centroid of Organization """ sql = f"SELECT * FROM public.tree WHERE organization_id = {organization_id}" df = gpd.read_postgis(sql, db.bind) X = df.geom.apply(lambda p: p.x) Y = df.geom.apply(lambda p: p.y) xCenter = np.sum(X) / len(X) yCenter = np.sum(Y) / len(Y) coordinate = Coordinate( longitude=xCenter, latitude=yCenter, ) return coordinate
def get_members( organization_id: int, *, auth=Depends(authorization("organizations:get_members")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): return crud.organization.get_members(db, id=organization_id)
def get_interventions( tree_id: int, *, auth=Depends(authorization("trees:get_interventions")), db: Session = Depends(get_db), ): """Get all interventions from a tree""" return crud.intervention.get_by_tree(db, tree_id)
def create( organization_id: int, *, auth=Depends(authorization("interventions:create")), request_intervention: InterventionCreate, db: Session = Depends(get_db), ): request_intervention.organization_id = organization_id return crud.intervention.create(db, obj_in=request_intervention)
async def upload_working_area( file: UploadFile = File(...), *, organization_id: int, auth=Depends(authorization("organizations:upload_working_area")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """ Upload a geo file """ try: filename_parts = os.path.splitext(file.filename) extension = filename_parts[1][1:] if extension not in ["geojson"]: raise HTTPException(status_code=415, detail="File format unsupported") unique_name = uuid.uuid4() unique_filename = f"{unique_name}.{extension}" copy_filename = f"{settings.UPLOADED_FILES_FOLDER}/{unique_filename}" copy_file = await file.read() with open(copy_filename, "wb") as f: f.write(copy_file) # type: ignore c = fiona.open(copy_filename) record = next(iter(c)) if not record["geometry"]["type"]: raise HTTPException(status_code=415, detail="File unsupported") organization_in_db = organization.get(db, id=organization_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Organization not found") shape_working_area = shape(record["geometry"]) working_area = shape_working_area.wkt if record["geometry"]["type"] == "Polygon": working_area = MultiPolygon([shape_working_area]).wkt return organization.update( db, db_obj=organization_in_db, obj_in={ "working_area": working_area }, ).to_schema() finally: os.remove(copy_filename) await file.close()
def read( health_assessment_id: int, *, auth=Depends(authorization("health_assessment:read")), db: Session = Depends(get_db), ): health_assessment = crud.health_assessment.get(db, health_assessment_id) if not health_assessment: raise HTTPException(status_code=404, detail=f"Health assesment with id *{health_assessment_id}* not found") return health_assessment
def delete( organization_id: int, tree_id: int, auth=Depends(authorization("trees:delete")), db: Session = Depends(get_db), ) -> Any: """Deletes a tree""" response = crud.crud_tree.tree.remove(db, id=tree_id).to_xy() return response
def get_path( organization_id: int, auth=Depends(authorization("organizations:get_path")), *, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): return [ org.to_schema() for org in organization.get_path(db, id=organization_id) ]
def update_member_role( organization_id: int, user_id: int, *, role: str = Body(..., embed=True), auth=Depends(authorization("organizations:edit_member")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): roles_order = [ "admin", "owner", "manager", "contributor", "reader", "guest" ] if role not in roles_order: raise HTTPException(status_code=400, detail=f"This role : {role} does not exist") user_in_db = user.get(db, id=user_id) if not user_in_db: raise HTTPException(status_code=404, detail="User not found") # Convert to sqlalchemy obj to UserOut schema for hidding password user_in_db = UserOut(**user_in_db.as_dict()) current_user_roles = enforcer.get_roles_for_user_in_domain( str(current_user.id), str(organization_id)) if len(current_user_roles) > 0: current_user_role = current_user_roles[0] if roles_order.index(current_user_role) >= roles_order.index(role): raise HTTPException(status_code=403, detail="You can only set roles below yours") user_roles = enforcer.get_roles_for_user_in_domain(str(user_id), str(organization_id)) # Role exist for this user (it's a patch) if len(user_roles) > 0: user_role = user_roles[0] if roles_order.index(current_user_role) >= roles_order.index( user_role): raise HTTPException(status_code=403, detail="You can't edit a role above yours") enforcer.delete_roles_for_user_in_domain(str(user_id), user_role, str(organization_id)) enforcer.add_role_for_user_in_domain(str(user_id), role, str(organization_id)) # need ro reload handle new policy enforcer.load_policy() return dict(user_in_db, role=role)
async def bulk_delete( organization_id: int, request: Request, trees: List[int] = Body(..., embed=True), db: Session = Depends(get_db), auth=Depends(authorization("trees:bulk_delete")), ) -> Any: """Bulk delete""" for tree_id in trees: crud.crud_tree.tree.remove(db, id=tree_id) return trees
def add_members( organization_id: int, *, invites: List[UserInvite] = Body(...), auth=Depends(authorization("organizations:add_members")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): try: members = crud.organization.get_members(db, id=organization_id) users_to_add = (invite for invite in invites if invite.email not in (u.get("email") for u in members)) for invite in users_to_add: user_in_db = user.get_by_email(db, email=invite.email) role = invite.role if invite.role else "guest" if not user_in_db: user_in_db = user.create( db, obj_in=UserCreate( full_name=invite.full_name, email=invite.email, password=pwd.genword(), ), ) else: organization_in_db = crud.organization.get(db, id=organization_id) if organization_in_db: send_new_invitation_email_task.delay( full_name=user_in_db.full_name, email_to=user_in_db.email, organization=organization_in_db.name, role=role) enforcer.add_role_for_user_in_domain( str(user_in_db.id), role, str(organization_id), ) enforcer.load_policy() return [ user for user in get_members( organization_id, auth=auth, db=db, current_user=current_user) if user.get("email") in (invite.email for invite in invites) ] except Exception as e: logging.error(e)
async def upload_geo_file( organization_id: int, auth=Depends(authorization("geofiles:upload_geo_file")), file: UploadFile = File(...), db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user), ): """ Upload a geo file """ try: filename_parts = os.path.splitext(file.filename) extension = filename_parts[1][1:] if not extension in settings.GEO_FILES_ALLOWED: raise HTTPException(status_code=415, detail="File format unsupported") unique_name = uuid.uuid4() unique_filename = f"{unique_name}.{extension}" copy_filename = f"{settings.UPLOADED_FILES_FOLDER}/{unique_filename}" copy_file = await file.read() with open(copy_filename, "wb") as f: f.write(copy_file) # type: ignore geofile = models.GeoFile( name=str(unique_name), original_name=file.filename, extension=extension, user_id=current_user.id, organization_id=organization_id, ) geofile_exists = crud.geo_file.get_by_checksum(db, checksum=geofile.checksum) if geofile_exists: os.remove(geofile.get_filepath(extended=False)) raise HTTPException( status_code=400, detail=f"The geofile with the {geofile.checksum} checksum already exists in the system.", ) if not geofile.is_valid(): raise HTTPException(status_code=415, detail="File corrupt") db.add(geofile) db.commit() finally: await file.close() return geofile
def trees_export( organization_id: int, format: str = "geojson", auth=Depends(authorization("trees:export")), db: Session = Depends(get_db), ) -> Any: sql = f"SELECT * FROM public.tree WHERE organization_id = {organization_id}" df = gpd.read_postgis(sql, db.bind) if df.empty: return HTTPException(status_code=404, detail="this organization has no trees") if format not in ["geojson", "csv", "xlsx"]: return HTTPException(status_code=404, detail="format not found") organization_in_db = crud.organization.get(db, id=organization_id) if organization_in_db is None: raise HTTPException(status_code=404, detail="organization not found") stream: Union[io.BytesIO, io.StringIO] = io.BytesIO() if format == "geojson": df.to_file(stream, driver="GeoJSON") media_type = "application/geo+json" if format in ["csv", "xlsx"]: df["lat"] = df.geom.y df["lng"] = df.geom.x df_properties = gpd.pd.DataFrame(df["properties"].values.tolist()) col = df.columns.difference(["properties"]) df = gpd.pd.concat([df[col], df_properties], axis=1) df = df.drop(columns=["geom"]) if format == "csv": stream = io.StringIO(df.to_csv()) media_type = "text/csv" if format == "xlsx": df.to_excel(stream, engine="xlsxwriter") media_type = "application/xlsx" response = StreamingResponse(iter([stream.getvalue()]), media_type=media_type) response.headers[ "Content-Disposition"] = f"attachment; filename=export.{format}" return response
def delete_images( tree_id: int, organization_id: int, auth=Depends(authorization("trees:delete_images")), ): """ delete all images """ upload_folder: str = f"{settings.UPLOADED_FILES_FOLDER}/organizations/{str(organization_id)}/{str(tree_id)}" try: shutil.rmtree(upload_folder) return HTMLResponse(status_code=200, content=f"{tree_id}") except Exception as e: logging.error(e) return HTTPException(status_code=500, detail="Can't delete folder")
def index( organization_id: int, tree_id: int, *, auth=Depends(authorization("health_assessment:read")), db: Session = Depends(get_db), ): """Get all Tree Health Assessment from a given tree""" organization = crud.organization.get(db, id=organization_id) if not organization: raise HTTPException(status_code=404, detail="Organization not found") tree = crud.tree.get(db, tree_id) if not tree: raise HTTPException(status_code=404, detail=f"Organization not found") return tree.health_assessments
def get_metrics_by_year( *, organization_id: int, year: int, auth=Depends(authorization("organizations:get_metrics")), db: Session = Depends(get_db), ) -> Any: """ get dashboard metrics from one organization """ organization_in_db = organization.get(db, id=organization_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Organization not found") trees_count = organization_in_db.total_trees # Asked in specs but no corresponding intervention_type planted_trees_count = crud.tree.get_planted( db, organization_id=organization_id, year=year) logged_trees_count = len( crud.intervention.get_done_by_type_and_year( db, organization_id=organization_id, intervention_type="felling", year=year)) scheduled_cost = 0 scheduled_interventions = crud.intervention.get_scheduled_by_year( db, organization_id=organization_id, year=year) for i in scheduled_interventions: scheduled_cost += i.estimated_cost done_cost = 0 done_interventions = crud.intervention.get_done_by_year( db, organization_id=organization_id, year=year) for i in done_interventions: done_cost += i.estimated_cost metrics = OrganizationMetrics( total_tree_count=trees_count, logged_trees_count=logged_trees_count, planted_trees_count=planted_trees_count, done_interventions_cost=done_cost, scheduled_interventions_cost=scheduled_cost, ) return metrics
def delete_image( image: str, tree_id: int, organization_id: int, auth=Depends(authorization("trees:delete_image")), ): """ delete one image """ image_path: str = f"{settings.UPLOADED_FILES_FOLDER}/organizations/{str(organization_id)}/{str(tree_id)}/{image}" try: if os.path.exists(image_path): os.remove(image_path) return HTMLResponse(status_code=200, content=f"{tree_id}") except: return HTTPException(status_code=500, detail="Can't delete {image} file")
def create( organization_id: int, tree_id: int, *, auth=Depends(authorization("health_assessment:create")), request_health_assessment: HealthAssessmentCreate, db: Session = Depends(get_db), ): organization_in_db = crud.organization.get(db, id=organization_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Organization not found") tree_in_db = crud.tree.get(db, id=tree_id) if not tree_in_db: raise HTTPException(status_code=404, detail="Tree not found") request_health_assessment.organization_id = organization_id request_health_assessment.tree_id = tree_id return crud.health_assessment.create(db, obj_in=request_health_assessment)
def read_geofile_by_name( organization_id: int, auth=Depends(authorization("geofiles:read_geofile_by_name")), *, db: Session = Depends(get_db), name: str, current_user: models.User = Depends(get_current_user), ) -> Any: """ Retrieve geo files. """ geofile = crud.geo_file.get_by_name(db, name=name) if not geofile: raise HTTPException( status_code=404, detail=f"The geofile with name {name} does not exist in the system", ) return geofile
def update_organization( organization_id: int, *, auth=Depends(authorization("organizations:update")), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), organization_in: OrganizationUpdate, ) -> Optional[OrganizationModel]: org = organization.get(db, id=organization_id) if not org: raise HTTPException(status_code=404, detail="Organization not found") organization_in_db = organization.update( db, db_obj=org, obj_in=jsonable_encoder(organization_in, exclude_unset=True), ) return organization_in_db.to_schema()
def read_geo_files( organization_id: int, auth=Depends(authorization("geofiles:read_geo_files")), db: Session = Depends(get_db), skip: int = 0, limit: int = 100, current_user: models.User = Depends(get_current_user), ) -> Any: """ Retrieve geo files. """ geo_files = crud.geo_file.get_multi( db, organization_id=organization_id, user_id=current_user.id, skip=skip, limit=limit, ) return geo_files
def get_working_area( *, organization_id: int, auth=Depends(authorization("organizations:get_working_area")), db: Session = Depends(get_db), ) -> Any: """ get working_area geojson from one organization """ organization_in_db = organization.get(db, id=organization_id) if not organization_in_db: raise HTTPException(status_code=404, detail="Organization not found") if organization_in_db.working_area is None: raise HTTPException(status_code=404, detail="Organization working area is empty") shape = to_shape(organization_in_db.working_area) return {"type": "Feature", "geometry": mapping(shape), "properties": {}}
def upload_images(tree_id: int, organization_id: int, auth=Depends(authorization("trees:upload_images")), images: List[UploadFile] = File(...)): """ upload pictures to a tree """ upload_folder: str = f"{settings.UPLOADED_FILES_FOLDER}/organizations/{str(organization_id)}/{str(tree_id)}" if os.path.exists(upload_folder) is False: os.makedirs(upload_folder) total = 0 for image in images: with open(f"{upload_folder}/{image.filename}", "wb") as buffer: if imghdr.what(image.file) in ['png', 'gif', 'jpeg']: shutil.copyfileobj(image.file, buffer) total += 1 return HTMLResponse(status_code=200, content=f"{total} images uploaded")