Skip to content

Commit

Permalink
Merge f1e449c into 9b65121
Browse files Browse the repository at this point in the history
  • Loading branch information
JoanaFigueira committed Apr 11, 2022
2 parents 9b65121 + f1e449c commit b2b6744
Show file tree
Hide file tree
Showing 15 changed files with 17,799 additions and 14 deletions.
70 changes: 70 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: CI

on:
push: # git push
pull_request: # PR
workflow_dispatch: # manual

jobs:
build:
runs-on: ubuntu-latest
env:
TRAVIS: 'true' # Skip tests requiring data
MONGO_URI: 'mongodb://localhost:27017/heman'
SENTRY_REDIS_HOST: http://localhost:6379
#SENTRY_DSN: 'http://localhost:6379'
services:
redis:
image: redis
ports:
- 6379:6379
#options: --entrypoint redis-server
sentry:
image: sentry
ports:
- 8099:80
mongo:
image: mongo
ports:
- 27017:27017
strategy:
matrix:
python-version:
- '2.7' # production
#- '3.8'
#- '3.9'
mongodb-version:
- 2
redis-version:
- 6
name: Python ${{ matrix.python-version }}
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements.txt # needed to use empowering and amoniak from git not from pypi
./setup.py develop
- uses: BSFishy/pip-action@v1
with:
packages: |
coveralls
- name: Unit tests
run: |
pytest
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop
with:
parallel: true
flag-name: Unit tests
coveralls_finish:
needs: build
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: AndreMiras/coveralls-python-action@develop
with:
parallel-finished: true
40 changes: 40 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
release:
types: [published]
workflow_dispatch:
push:
tags:
- 'heman-[0-9]+.[0-9]+.[0-9]+'

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# He-Man

Uses the Empowering Sword (a.k.a Empowering Proxy API for users).

He-Man provides an authentified API to access several information regarding Som Energia users stored as objects in monogdb.

- CCH curves from distribution
- Photovoltaic simulations
- Infoenergia profiles

## Develoment Deployment

```bash
# In different consoles run
docker run -it --rm -p 27017:27017 mongo
docker run -it --rm -p 6379:6379 redis

# then in your main one
./setup.py develop

# to run the test
pytest

# to deploy locally
MONGO_URI=mongodb://localhost python run_api.py

```

Updating the

## Test data

To obtain test data:

curl 'https://api.beedataanalytics.com/v1/components?where="contractId"=="XXXXXXX" and "type"=="FV"'

curl 'https://api.beedataanalytics.com/authn/login'


## TODO

- Discovering tests from heman/ fails since the api is created twice
- Py3: Migrate to sentry-sdk, since raven library raven library is not Py3.


4 changes: 0 additions & 4 deletions README.rst

This file was deleted.

2 changes: 1 addition & 1 deletion heman/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
try:
VERSION = __import__('pkg_resources') \
.get_distribution(__name__).version
except Exception, e:
except Exception as e:
VERSION = 'unknown'
180 changes: 180 additions & 0 deletions heman/api/pvcalculator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import json
import pymongo

from flask import current_app, jsonify, Response
from flask_restful import request

from heman.api import AuthorizedResource
from heman.auth import check_contract_allowed
from heman.config import mongo


class PVCalculatorResource(AuthorizedResource):
method_decorators = (
AuthorizedResource.method_decorators + [check_contract_allowed]
)

def options(self, *args, **kwargs):
return jsonify({})

def get_last_scenario(self, contract_name):
return mongo.db['photovoltaic_reports'].find_one(
{'contractName': contract_name},
sort=[('id', pymongo.DESCENDING)]
)


def parseMongoAzimuth(azimuth, gabledroof):
"""
This function turns mongo representation of azimuth into a int tuple.
Currently, Mongo objects represents azimuths either as a float like 120.0
or as a string like "120#300" when it is a gabled roof.
The first azimuth is always the one towards south or 90 if W-E orientation.
TODO: change the mongo representation to be a more uniform one.
"""
if not gabledroof:
return (int(azimuth),)
return (int(azimuth), int((azimuth+180) % 360))

def queryPeakPower(peakPower):
if not peakPower: return None
return float(peakPower)

def queryAzimuth(queryAzimuth):
"""
This turns a list of strings representing the azimuths into
a hashable tuple of ints.
"""
return tuple(int(a) for a in queryAzimuth)

class ScenarioReport(PVCalculatorResource):

"""Given some parameter values chooses the matching scenario with least payback
"""

def get(self, contract):
current_app.logger.debug('PVCalculator Report, contract {}'.format(contract))

tiltDegrees = float(request.args.get('tilt'))
azimuthDegrees = queryAzimuth(request.args.getlist('azimuth'))
peakPowerKw = queryPeakPower(request.args.get('power'))

scenario_report = self.get_last_scenario(contract_name=contract)

if not scenario_report:
return Response({}, mimetype='application/json')

try:
scenarios = scenario_report['results']['pvAutoSize']['scenarios']
totalLoad = scenario_report['results']['pvAutoSize']['load']['total']
except KeyError as e:
print("Error {}", e)
return Response({}, mimetype='application/json')
except IndexError as e:
print("Error {}", e)
return Response({}, mimetype='application/json')

selectedScenarios = [
scenario
for i,scenario in enumerate(scenarios)
if scenario['settings']['tilt'] == tiltDegrees
and parseMongoAzimuth(
scenario['settings']['azimuth'],
scenario['settings']['gabledroof']
) == azimuthDegrees
and (scenario['settings']['power'] == peakPowerKw or not peakPowerKw)
]
if not selectedScenarios:
return Response(
json.dumps(dict(
error='NOT_FOUND',
message='Scenario not found',
)),
mimetype='application/json',
)

bestScenario = min(
selectedScenarios,
key=lambda s: s['economics']['payback'],
)

result = dict(
loadKwhYear = totalLoad,
loadByPeriodKwh = scenario_report['results']['pvAutoSize']['load']['timeslots'],
productionKwhYear = bestScenario['generation']['total'],
productionToLoadKwhYear = bestScenario['generation']['PVtoLoad'],
productionToLoadEuroYear = bestScenario['generation']['PVtoLoadCost'],
productionToLoadPercent = bestScenario['generation']['PVtoLoadPct'],
productionToGridKwhYear = bestScenario['generation']['PVtoGrid'],
productionToGridEuroYear = bestScenario['generation']['PVtoGridCost'],
productionToGridPercent = bestScenario['generation']['PVtoGridPct'],
loadFromGridKwhYear = bestScenario['generation']['loadFromGrid'],
savingsEuroYear = bestScenario['generation']['savings'],
paybackYears = bestScenario['economics']['payback'],
installationCostEuro = bestScenario['settings']['cost'],
azimuthDegrees= parseMongoAzimuth(
bestScenario['settings']['azimuth'],
bestScenario['settings']['gabledroof']
),
tiltDegrees= bestScenario['settings']['tilt'],
areaM2 = bestScenario['settings']['area'],
nModules = bestScenario['settings']['numModules'],
peakPowerKw = bestScenario['settings']['power'],
dailyLoadProfileKwh = scenario_report['results']['pvAutoSize']['load']['profile'],
dailyProductionProfileKwh = bestScenario['generation']['profile'],
monthlyProductionToLoadKwh = bestScenario['generation']['monthlyPVtoLoad'],
monthlyProductionToLoadEuro = bestScenario['generation']['monthlyPVtoLoadCost'],
monthlyGridToLoadKwh = bestScenario['generation']['monthlyLoadFromGrid'],
monthlyGridToLoadEuro = bestScenario['generation']['monthlyLoadFromGridCost'],
monthlyProductionToGridKwh = bestScenario['generation']['monthlyPVtoGrid'],
monthlyProductionToGridEuro = bestScenario['generation']['monthlyPVtoGridCost'],
monthlyProductionKwh = bestScenario['generation']['monthlyPV'],
#monthlyProductionEuro = bestScenario['generation']['monthlyPVCost'], # TODO: Info not yet available
)

return Response(json.dumps(result), mimetype='application/json')


class ScenarioParams(PVCalculatorResource):

"""Returns the parameters available values to choose scenario"""

def get(self, contract):

scenario_report = self.get_last_scenario(contract_name=contract)
if not scenario_report:
return Response({}, mimetype='application/json')

try:
scenarios = scenario_report['results']['pvAutoSize']['scenarios']
except KeyError as e:
print("Error {}", e)
return Response({}, mimetype='application/json')
except IndexError as e:
print("Error {}", e)
return Response({}, mimetype='application/json')

tilts, azimuths, powers = zip(*[
(
scenario['settings']['tilt'],
parseMongoAzimuth(
scenario['settings']['azimuth'],
scenario['settings']['gabledroof']
),
scenario['settings']['power'],
)
for i,scenario in enumerate(scenarios)
])
result = dict(
tilt = list(sorted(set(tilts))),
azimuth = list(sorted(set(azimuths))),
power = list(sorted(set(powers))),
)
return Response(json.dumps(result), mimetype='application/json')


resources = [
(ScenarioReport, '/ScenarioReport/<contract>'),
(ScenarioParams, '/ScenarioParams/<contract>'),

]
Loading

0 comments on commit b2b6744

Please sign in to comment.