예제 #1
0
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
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
def delete(
        id: int,
        *,
        auth=Depends(authorization("interventions:delete")),
        db: Session = Depends(get_db),
):
    return crud.intervention.remove(db, id=id)
예제 #7
0
def get(
        intervention_id: int,
        *,
        auth=Depends(authorization("interventions:get")),
        db: Session = Depends(get_db),
):
    return crud.intervention.get(db, id=intervention_id)
예제 #8
0
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
예제 #9
0
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)
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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()
예제 #13
0
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
예제 #14
0
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
예제 #15
0
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)
    ]
예제 #16
0
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)
예제 #17
0
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
예제 #18
0
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)
예제 #19
0
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
예제 #20
0
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
예제 #21
0
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")
예제 #22
0
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
예제 #23
0
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
예제 #24
0
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")
예제 #25
0
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)
예제 #26
0
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
예제 #27
0
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()
예제 #28
0
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
예제 #29
0
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": {}}
예제 #30
0
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")