Merge branch 'master' into develop
This commit is contained in:
commit
97953db742
.gitignore
app
map_styles
src
docs
etl/join_building_data
maintenance/extract_data
migrations
011.sustainability.down1-extra.sql011.sustainability.up1-extra.sql012.community.down.sql012.community.up.sql016.landuse.down.sql0xx.construction-materials.down.sql0xx.construction-materials.up.sql0xx.construction.down.sql0xx.team.down.sql0xx.team.up.sql0xx.type-extend.down.sql0xx.type-extend.up.sqlREADME.md
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,6 +18,8 @@ etl/**/*.xls
|
||||
etl/**/*.xlsx
|
||||
etl/**/*.zip
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Cache
|
||||
app/tilecache/**/*.png
|
||||
app/tilecache/**/*.mbtiles
|
||||
|
@ -123,6 +123,40 @@
|
||||
<PolygonSymbolizer fill="#980043" />
|
||||
</Rule>
|
||||
</Style>
|
||||
<Style name="construction_core_material">
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Wood"</Filter>
|
||||
<PolygonSymbolizer fill="#96613b" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Stone"</Filter>
|
||||
<PolygonSymbolizer fill="#ffffe3" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Brick"</Filter>
|
||||
<PolygonSymbolizer fill="#f5d96b" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Steel"</Filter>
|
||||
<PolygonSymbolizer fill="#beffe8" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Reinforced Concrete"</Filter>
|
||||
<PolygonSymbolizer fill="#fca89d" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Other Metal"</Filter>
|
||||
<PolygonSymbolizer fill="#5c8970" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Other Natural Material"</Filter>
|
||||
<PolygonSymbolizer fill="#b5a859" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[construction_core_material] = "Other Man-Made Material"</Filter>
|
||||
<PolygonSymbolizer fill="#c48a85" />
|
||||
</Rule>
|
||||
</Style>
|
||||
<Style name="date_year">
|
||||
<Rule>
|
||||
<Filter>[date_year] >= 2020</Filter>
|
||||
@ -317,7 +351,7 @@
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[current_landuse_order] = "Transport"</Filter>
|
||||
<PolygonSymbolizer fill="#b3de69" />
|
||||
<PolygonSymbolizer fill="#bee8ff" />
|
||||
</Rule>
|
||||
<Rule>
|
||||
<Filter>[current_landuse_order] = "Utilities And Infrastructure"</Filter>
|
||||
|
@ -7,11 +7,11 @@ import { ApiParamError, ApiUserError } from './errors/api';
|
||||
import { DatabaseError } from './errors/general';
|
||||
import buildingsRouter from './routes/buildingsRouter';
|
||||
import extractsRouter from './routes/extractsRouter';
|
||||
import leaderboardRouter from './routes/leaderboardRouter';
|
||||
import usersRouter from './routes/usersRouter';
|
||||
import { queryLocation } from './services/search';
|
||||
import { authUser, getNewUserAPIKey, logout } from './services/user';
|
||||
|
||||
|
||||
const server = express.Router();
|
||||
|
||||
// parse POSTed json body
|
||||
@ -20,6 +20,7 @@ server.use(bodyParser.json());
|
||||
server.use('/buildings', buildingsRouter);
|
||||
server.use('/users', usersRouter);
|
||||
server.use('/extracts', extractsRouter);
|
||||
server.use('/leaderboard', leaderboardRouter);
|
||||
|
||||
server.get('/history', editHistoryController.getGlobalEditHistory);
|
||||
server.get('/autofill', autofillController.getAutofillOptions);
|
||||
|
22
app/src/api/controllers/leaderboardController.ts
Normal file
22
app/src/api/controllers/leaderboardController.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import express from 'express';
|
||||
|
||||
import asyncController from "../routes/asyncController";
|
||||
import * as leadersService from '../services/leaderboard';
|
||||
|
||||
const getLeaders = asyncController(async (req: express.Request, res: express.Response) => {
|
||||
try {
|
||||
const number_limit = req.query.number_limit;
|
||||
const time_limit = req.query.time_limit;
|
||||
const result = await leadersService.getLeaders(number_limit, time_limit);
|
||||
res.send({
|
||||
leaders: result
|
||||
});
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
res.send({ error: 'Database error' });
|
||||
}
|
||||
});
|
||||
|
||||
export default{
|
||||
getLeaders
|
||||
};
|
9
app/src/api/routes/leaderboardRouter.ts
Normal file
9
app/src/api/routes/leaderboardRouter.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import express from 'express';
|
||||
|
||||
import leaderboardController from '../controllers/leaderboardController';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/leaders', leaderboardController.getLeaders);
|
||||
|
||||
export default router;
|
@ -311,6 +311,9 @@ const BUILDING_FIELD_WHITELIST = new Set([
|
||||
'size_floor_area_ground',
|
||||
'size_floor_area_total',
|
||||
'size_width_frontage',
|
||||
'construction_core_material',
|
||||
'construction_secondary_materials',
|
||||
'construction_roof_covering',
|
||||
'planning_portal_link',
|
||||
'planning_in_conservation_area',
|
||||
'planning_conservation_area_name',
|
||||
|
39
app/src/api/services/leaderboard.ts
Normal file
39
app/src/api/services/leaderboard.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import db from '../../db';
|
||||
|
||||
async function getLeaders(number_limit: number, time_limit: number) {
|
||||
try {
|
||||
if(time_limit > 0){
|
||||
return await db.manyOrNone(
|
||||
`SELECT count(log_id) as number_edits, username
|
||||
FROM logs, users
|
||||
WHERE logs.user_id=users.user_id
|
||||
AND CURRENT_TIMESTAMP::DATE - log_timestamp::DATE <= $1
|
||||
AND NOT (users.username = 'casa_friendly_robot')
|
||||
AND NOT (users.username = 'colouringlondon')
|
||||
GROUP by users.username
|
||||
ORDER BY number_edits DESC
|
||||
LIMIT $2`, [time_limit, number_limit]
|
||||
);
|
||||
|
||||
}else{
|
||||
return await db.manyOrNone(
|
||||
`SELECT count(log_id) as number_edits, username
|
||||
FROM logs, users
|
||||
WHERE logs.user_id=users.user_id
|
||||
AND NOT (users.username = 'casa_friendly_robot')
|
||||
AND NOT (users.username = 'colouringlondon')
|
||||
GROUP by users.username
|
||||
ORDER BY number_edits DESC
|
||||
LIMIT $1`, [number_limit]
|
||||
);
|
||||
}
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
getLeaders
|
||||
};
|
@ -14,6 +14,7 @@ import ContactPage from './pages/contact';
|
||||
import ContributorAgreementPage from './pages/contributor-agreement';
|
||||
import DataAccuracyPage from './pages/data-accuracy';
|
||||
import DataExtracts from './pages/data-extracts';
|
||||
import LeaderboardPage from './pages/leaderboard';
|
||||
import OrdnanceSurveyLicencePage from './pages/ordnance-survey-licence';
|
||||
import OrdnanceSurveyUprnPage from './pages/ordnance-survey-uprn';
|
||||
import PrivacyPolicyPage from './pages/privacy-policy';
|
||||
@ -113,6 +114,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
<Route exact path="/data-accuracy.html" component={DataAccuracyPage} />
|
||||
<Route exact path="/data-extracts.html" component={DataExtracts} />
|
||||
<Route exact path="/contact.html" component={ContactPage} />
|
||||
<Route exact path="/leaderboard.html" component={LeaderboardPage} />
|
||||
<Route exact path="/history.html" component={ChangesPage} />
|
||||
<Route exact path={App.mapAppPaths} render={(props) => (
|
||||
<MapApp
|
||||
|
@ -74,9 +74,8 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
||||
return <ConstructionContainer
|
||||
{...props}
|
||||
title="Construction"
|
||||
intro="How are buildings built? Coming soon…"
|
||||
intro="How are buildings built?"
|
||||
help="https://pages.colouring.london/construction"
|
||||
inactive={true}
|
||||
/>;
|
||||
case 'team':
|
||||
return <TeamContainer
|
||||
|
@ -60,7 +60,7 @@ const Categories: React.FC<CategoriesProps> = (props) => (
|
||||
desc="Methods & materials"
|
||||
slug="construction"
|
||||
help="https://pages.colouring.london/construction"
|
||||
inactive={true}
|
||||
inactive={false}
|
||||
mode={props.mode}
|
||||
building_id={props.building_id}
|
||||
/>
|
||||
|
@ -1,55 +1,74 @@
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import { dataFields } from '../../data_fields';
|
||||
import DataEntry from '../data-components/data-entry';
|
||||
import SelectDataEntry from '../data-components/select-data-entry';
|
||||
import withCopyEdit from '../data-container';
|
||||
|
||||
import { CategoryViewProps } from './category-view-props';
|
||||
|
||||
const ConstructionMaterialsOptions = [
|
||||
'Wood',
|
||||
'Stone',
|
||||
'Brick',
|
||||
'Steel',
|
||||
'Reinforced Concrete',
|
||||
'Other Metal',
|
||||
'Other Natural Material',
|
||||
'Other Man-Made Material'
|
||||
];
|
||||
|
||||
const RoofCoveringOptions = [
|
||||
'Slate',
|
||||
'Clay Tile',
|
||||
'Wood',
|
||||
'Asphalt',
|
||||
'Iron or Steel',
|
||||
'Other Metal',
|
||||
'Other Natural Material',
|
||||
'Other Man-Made Material'
|
||||
];
|
||||
|
||||
/**
|
||||
* Construction view/edit section
|
||||
*/
|
||||
const ConstructionView = (props) => (
|
||||
<Fragment>
|
||||
<p className="data-intro">{props.intro}</p>
|
||||
<ul>
|
||||
<li>Construction system</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_system",
|
||||
// "type": "text"
|
||||
}
|
||||
<li>Primary materials</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_primary_material",
|
||||
// "type": "text"
|
||||
}
|
||||
<li>Secondary materials</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_secondary_material",
|
||||
// "type": "text"
|
||||
}
|
||||
<li>Roofing material</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_roofing_material",
|
||||
// "type": "text"
|
||||
}
|
||||
<li>Percentage of facade glazed</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_facade_percentage_glazed",
|
||||
// "type": "number",
|
||||
// "step": 5
|
||||
}
|
||||
<li>BIM reference or link</li>
|
||||
{
|
||||
// "disabled": true,
|
||||
// "slug": "construction_bim_reference",
|
||||
// "type": "text",
|
||||
// "placeholder": "https://..."
|
||||
}
|
||||
</ul>
|
||||
</Fragment>
|
||||
);
|
||||
const ConstructionView: React.FunctionComponent<CategoryViewProps> = (props) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<SelectDataEntry
|
||||
title={dataFields.construction_core_material.title}
|
||||
slug="construction_core_material"
|
||||
value={props.building.construction_core_material} // check
|
||||
tooltip={dataFields.construction_core_material.tooltip}
|
||||
options={ConstructionMaterialsOptions}
|
||||
mode={props.mode}
|
||||
copy={props.copy}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
<SelectDataEntry
|
||||
title={dataFields.construction_secondary_materials.title}
|
||||
slug="construction_secondary_materials"
|
||||
value={props.building.construction_secondary_materials} // check
|
||||
tooltip={dataFields.construction_secondary_materials.tooltip}
|
||||
options={ConstructionMaterialsOptions}
|
||||
mode={props.mode}
|
||||
copy={props.copy}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
<SelectDataEntry
|
||||
title={dataFields.construction_roof_covering.title}
|
||||
slug="construction_roof_covering"
|
||||
value={props.building.construction_roof_covering} // check
|
||||
tooltip={dataFields.construction_roof_covering.tooltip}
|
||||
options={RoofCoveringOptions}
|
||||
mode={props.mode}
|
||||
copy={props.copy}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ConstructionContainer = withCopyEdit(ConstructionView);
|
||||
|
||||
export default ConstructionContainer;
|
||||
|
@ -280,6 +280,24 @@ export const dataFields = {
|
||||
//tooltip: ,
|
||||
},
|
||||
|
||||
construction_core_material: {
|
||||
category: Category.Construction,
|
||||
title: "Core Material",
|
||||
tooltip:"The main structural material",
|
||||
},
|
||||
|
||||
construction_secondary_materials: {
|
||||
category: Category.Construction,
|
||||
title: "Secondary Construction Material/s",
|
||||
tooltip:"Other construction materials",
|
||||
},
|
||||
|
||||
construction_roof_covering: {
|
||||
category: Category.Construction,
|
||||
title: "Main Roof Covering",
|
||||
tooltip:'Main roof covering material',
|
||||
},
|
||||
|
||||
sust_breeam_rating: {
|
||||
category: Category.Sustainability,
|
||||
title: "BREEAM Rating",
|
||||
|
@ -89,6 +89,11 @@ class Header extends React.Component<HeaderProps, HeaderState> {
|
||||
Downloads
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink to="/leaderboard.html" className="nav-link" onClick={this.handleNavigate}>
|
||||
Leaderboard
|
||||
</NavLink>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<NavLink to="/contact.html" className="nav-link" onClick={this.handleNavigate}>
|
||||
Contact
|
||||
|
@ -75,7 +75,16 @@ const LEGEND_CONFIG = {
|
||||
},
|
||||
construction: {
|
||||
title: 'Construction',
|
||||
elements: []
|
||||
elements: [
|
||||
{ color: "#96613b", text: "Wood" },
|
||||
{ color: "#ffffe3", text: "Stone" },
|
||||
{ color: "#f5d96b", text: "Brick" },
|
||||
{ color: "#beffe8", text: "Steel" },
|
||||
{ color: "#fca89d", text: "Reinforced Concrete" },
|
||||
{ color: "#5c8970", text: "Other Metal" },
|
||||
{ color: "#b5a859", text: "Other Natural Material" },
|
||||
{ color: "#c48a85", text: "Other Man-Made Material" }
|
||||
]
|
||||
},
|
||||
team: {
|
||||
title: 'Team',
|
||||
|
@ -131,6 +131,7 @@ class ColouringMap extends Component<ColouringMapProps, ColouringMapState> {
|
||||
const tilesetByCat = {
|
||||
age: 'date_year',
|
||||
size: 'size_height',
|
||||
construction: 'construction_core_material',
|
||||
location: 'location',
|
||||
like: 'likes',
|
||||
planning: 'planning_combined',
|
||||
|
34
app/src/frontend/pages/leaderboard.css
Normal file
34
app/src/frontend/pages/leaderboard.css
Normal file
@ -0,0 +1,34 @@
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
margin-right: 20%;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
table th, td {
|
||||
border: 1px solid black;
|
||||
text-align: left;
|
||||
padding-left: 1%;
|
||||
}
|
||||
|
||||
table tr:nth-child(odd) {
|
||||
background: #f6f8fa;
|
||||
}
|
||||
|
||||
table tr:nth-child(1) {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#title {
|
||||
text-align: center;
|
||||
padding-bottom: 1%;
|
||||
}
|
||||
|
||||
#radiogroup {
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
margin: 0 2px 0 10px;
|
||||
}
|
149
app/src/frontend/pages/leaderboard.tsx
Normal file
149
app/src/frontend/pages/leaderboard.tsx
Normal file
@ -0,0 +1,149 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import './leaderboard.css';
|
||||
|
||||
interface Leader {
|
||||
number_edits: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
|
||||
interface LeaderboardProps {
|
||||
}
|
||||
|
||||
|
||||
interface LeaderboardState {
|
||||
leaders: Leader[];
|
||||
fetching: boolean;
|
||||
|
||||
//We need to track the state of the radio buttons to ensure their current state is shown correctly when the view is (re)rendered
|
||||
number_limit: number;
|
||||
time_limit: number;
|
||||
}
|
||||
|
||||
|
||||
class LeaderboardPage extends Component<LeaderboardProps, LeaderboardState> {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
leaders: [],
|
||||
fetching: false,
|
||||
number_limit: 10,
|
||||
time_limit: -1
|
||||
};
|
||||
this.getLeaders = this.getLeaders.bind(this);
|
||||
this.renderTableData = this.renderTableData.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
|
||||
|
||||
handleChange(e) {
|
||||
if(e.target.name == 'number_limit'){
|
||||
this.getLeaders(e.target.value, this.state.time_limit);
|
||||
this.setState({number_limit: e.target.value});
|
||||
}else {
|
||||
this.getLeaders(this.state.number_limit, e.target.value);
|
||||
this.setState({time_limit: e.target.value});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
this.getLeaders(this.state.number_limit, this.state.time_limit);
|
||||
}
|
||||
|
||||
|
||||
componentWillUnmount() {}
|
||||
|
||||
|
||||
getLeaders(number_limit, time_limit) {
|
||||
|
||||
this.setState({
|
||||
fetching: true
|
||||
});
|
||||
|
||||
fetch(
|
||||
'/api/leaderboard/leaders?number_limit=' + number_limit + '&time_limit='+time_limit
|
||||
).then(
|
||||
(res) => res.json()
|
||||
).then((data) => {
|
||||
if (data && data.leaders){
|
||||
this.setState({
|
||||
leaders: data.leaders,
|
||||
fetching: false
|
||||
});
|
||||
|
||||
} else {
|
||||
console.error(data);
|
||||
|
||||
this.setState({
|
||||
leaders: [],
|
||||
fetching: false
|
||||
});
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
|
||||
this.setState({
|
||||
leaders: [],
|
||||
fetching: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
renderTableData() {
|
||||
return this.state.leaders.map((u, i) => {
|
||||
const username = u.username;
|
||||
const number_edits = u.number_edits;
|
||||
|
||||
return (
|
||||
<tr key={username}>
|
||||
<td>{i+1}</td>
|
||||
<td>{username}</td>
|
||||
<td>{number_edits}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return(
|
||||
<div>
|
||||
<form id="radiogroup">
|
||||
<div id="number-radiogroup" >
|
||||
<p>Select number of users to be displayed: <br/>
|
||||
<input type="radio" name="number_limit" value="10" onChange={this.handleChange} checked={10 == this.state.number_limit} />10
|
||||
<input type="radio" name="number_limit" value="100" onChange={this.handleChange} checked={100 == this.state.number_limit} />100
|
||||
</p>
|
||||
</div>
|
||||
<div id="time-radiogroup" >
|
||||
<p>Select time period: <br/>
|
||||
<input type="radio" name="time_limit" value="-1" onChange={this.handleChange} checked={-1 == this.state.time_limit} /> All time
|
||||
<input type="radio" name="time_limit" value="7" onChange={this.handleChange} checked={7 == this.state.time_limit} /> Last 7 days
|
||||
<input type="radio" name="time_limit" value="30" onChange={this.handleChange} checked={30 == this.state.time_limit} /> Last 30 days
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<h1 id='title'>Leader Board</h1>
|
||||
<table id='leaderboard'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Username</th>
|
||||
<th>Contributions</th>
|
||||
</tr>
|
||||
{this.renderTableData()}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default LeaderboardPage;
|
||||
|
@ -55,6 +55,15 @@ const BUILDING_LAYER_DEFINITIONS = {
|
||||
buildings as b
|
||||
WHERE g.geometry_id = b.geometry_id
|
||||
) as size_height`,
|
||||
construction_core_material: `(
|
||||
SELECT
|
||||
b.core_materials,
|
||||
g.geometry_geom
|
||||
FROM
|
||||
geometries as g,
|
||||
buildings as b
|
||||
WHERE g.geometry_id = b.geometry_id
|
||||
) as core_materials`,
|
||||
location: `(
|
||||
SELECT
|
||||
(
|
||||
|
@ -1,35 +1,52 @@
|
||||
## Adding new building attribute fields
|
||||
# Adding new building attribute fields
|
||||
|
||||
This document is a checklist for adding a new building attribute to the system. It's split into two sections - actions that apply when adding any field, and additional steps to add a field that will be visualised on the map.
|
||||
This document is a checklist for adding a new building attribute to the system. It's split into three sections - actions that apply when adding any field, and additional steps to add a field that will be visualised on the map.
|
||||
The second section would be required when adding a new category or when changing which field should be visualised for a category.
|
||||
The third section would appply to any data which can be ammended via the API.
|
||||
When adding a new attribute a set of seed data should be identified, the base data set formany fields is Polly Hudsons PhD data set. This data set is required to;
|
||||
- Test visualisation elements (map styling)
|
||||
- Provide some data for users to relate to and encourage them to fill in the field
|
||||
- Test the API and database elements.
|
||||
|
||||
### Adding any attribute
|
||||
|
||||
#### In database
|
||||
## Adding any attribute
|
||||
|
||||
### In database
|
||||
1. Add a column to the `buildings` table in the database.
|
||||
2. Add any check constraints or foreign key constraints on the column, if necessary (if the foreign key constraint is used to restrict the column to a set of values, the table with the values might need to be created from scratch)
|
||||
3. If a source is being collected for field. Add a column `fieldName_source` to the `sources` table.
|
||||
4. If verfication is being enabled. Add a column `bieldName_verifications` to the `verfication` table.
|
||||
|
||||
#### In API
|
||||
|
||||
### In API
|
||||
1. Add field name to `BUILDING_FIELD_WHITELIST` in the building service to allow saving changes to the field
|
||||
2. Add any special domain logic for processing updates to the field in the `processBuildingUpdate()` function
|
||||
|
||||
#### In frontend
|
||||
### In frontend
|
||||
1. Add the field description to the `dataFields` object in `data_fields.ts`
|
||||
2. Add the data entry React component of the appropriate type (text, numeric etc) to the appropriate category view component in the `building/data-containers` folder. Link to `dataFields` for static string values (field name, description etc)
|
||||
|
||||
|
||||
#### In data extracts
|
||||
### In data extracts
|
||||
1. Add the field to the select list in the COPY query in `maintenance/extract_data/export_attributes.sql`
|
||||
2. Add a description of the field to the `README.txt` file
|
||||
|
||||
### Adding an attribute which is used to colour the map
|
||||
## Adding an attribute which is used to colour the map
|
||||
|
||||
All steps from the previous section need to be carried out first.
|
||||
|
||||
#### In tileserver
|
||||
1. Add a SQL query for calculating the value to be visualised to `BUILDING_LAYER_DEFINITIONS` in `app/tiles/dataDefinition.ts`
|
||||
### In tileserver
|
||||
1. Add a SQL query for calculating the value to be visualised to `BUILDING_LAYER_DEFINITIONS` in `app/src/tiles/dataDefinition.ts`
|
||||
2. Add Mapnik rendering style in `app/map_styles/polygon.xml`
|
||||
|
||||
#### In frontend
|
||||
### In frontend
|
||||
1. Update the category to field name mapping in the `tilesetByCat` object inside the `ColouringMap` React component (`map.tsx` file)
|
||||
2. Add an entry for the field to the `LEGEND_CONFIG` object in `legend.tsx` file
|
||||
2. Add an entry for the field to the `LEGEND_CONFIG` object in `legend.tsx` file
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
Run tests on staging to confirm;
|
||||
- Database changes accepted
|
||||
- API is working and data is getting posted to the database
|
||||
- Map styling is applied and style is appropriate way to visualise the data
|
||||
|
@ -60,9 +60,14 @@ def main(base_url, api_key, source_file, json_columns):
|
||||
if building_id is None:
|
||||
continue
|
||||
|
||||
if 'sust_dec' in line and line['sust_dec'] == '':
|
||||
del line['sust_dec']
|
||||
|
||||
response_code, response_data = update_building(building_id, line, api_key, base_url)
|
||||
if response_code != 200:
|
||||
print('ERROR', building_id, response_code, response_data)
|
||||
else:
|
||||
print('DEBUG', building_id, response_code, response_data)
|
||||
|
||||
|
||||
def update_building(building_id, data, api_key, base_url):
|
||||
@ -82,6 +87,7 @@ def find_building(data, base_url):
|
||||
if building_id is not None:
|
||||
print("match_by_building_id", building_id)
|
||||
return building_id
|
||||
|
||||
if 'toid' in data:
|
||||
building_id = find_by_reference(base_url, 'toid', data['toid'])
|
||||
if building_id is not None:
|
||||
|
@ -61,6 +61,11 @@ def update_building(building_id, data, api_key, base_url):
|
||||
|
||||
|
||||
def find_building(data, base_url):
|
||||
if 'building_id' in data:
|
||||
building_id = data['building_id']
|
||||
if building_id is not None:
|
||||
print("match_by_building_id", building_id)
|
||||
return building_id
|
||||
if 'toid' in data:
|
||||
building_id = find_by_reference(base_url, 'toid', data['toid'])
|
||||
if building_id is not None:
|
||||
|
140
etl/join_building_data/load_shapefile_to_staging.py
Normal file
140
etl/join_building_data/load_shapefile_to_staging.py
Normal file
@ -0,0 +1,140 @@
|
||||
"""Join shapefile data to buildings
|
||||
|
||||
This is effectively an example script using the HTTP API, tailored to particular collected
|
||||
datasets for Camden (age data) and Fitzrovia (number of storeys).
|
||||
|
||||
- read through shapes
|
||||
- locate building by toid
|
||||
- else locate building by representative point
|
||||
- update building with data
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
import fiona
|
||||
import pyproj
|
||||
import requests
|
||||
from shapely.geometry import shape
|
||||
from shapely.ops import transform
|
||||
|
||||
|
||||
osgb_to_ll = partial(
|
||||
pyproj.transform,
|
||||
pyproj.Proj(init='epsg:27700'),
|
||||
pyproj.Proj(init='epsg:4326')
|
||||
)
|
||||
|
||||
|
||||
def main(base_url, api_key, process, source_file):
|
||||
"""Read from file, update buildings
|
||||
"""
|
||||
with fiona.open(source_file, 'r') as source:
|
||||
for feature in source:
|
||||
props = feature['properties']
|
||||
|
||||
if process == "camden":
|
||||
toid, data = process_camden(props)
|
||||
else:
|
||||
toid, data = process_fitzrovia(props)
|
||||
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
building_id = find_building(toid, feature['geometry'], base_url)
|
||||
if not building_id:
|
||||
print("no_match", toid, "-")
|
||||
continue
|
||||
|
||||
save_data(building_id, data, api_key, base_url)
|
||||
|
||||
|
||||
def process_camden(props):
|
||||
toid = osgb_toid(props['TOID'])
|
||||
data = {
|
||||
'date_year': props['Year_C'],
|
||||
'date_source_detail': props['Date_sou_1']
|
||||
}
|
||||
return toid, data
|
||||
|
||||
|
||||
def process_fitzrovia(props):
|
||||
toid = osgb_toid(props['TOID'])
|
||||
storeys = props['Storeys']
|
||||
|
||||
if storeys is None:
|
||||
return toid, None
|
||||
|
||||
if props['Basement'] == 'Yes':
|
||||
data = {
|
||||
'size_storeys_core': int(storeys) - 1,
|
||||
'size_storeys_basement': 1
|
||||
}
|
||||
else:
|
||||
data = {
|
||||
'size_storeys_core': int(storeys),
|
||||
'size_storeys_basement': 0
|
||||
}
|
||||
return toid, data
|
||||
|
||||
|
||||
def osgb_toid(toid):
|
||||
if toid is None:
|
||||
toid = ""
|
||||
return "osgb" + toid.lstrip("0")
|
||||
|
||||
|
||||
def save_data(building_id, data, api_key, base_url):
|
||||
"""Save data to a building
|
||||
"""
|
||||
r = requests.post(
|
||||
"{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
|
||||
json=data
|
||||
)
|
||||
|
||||
|
||||
def find_building(toid, geom, base_url):
|
||||
"""Find building_id by TOID or location
|
||||
"""
|
||||
r = requests.get(base_url + "/buildings/reference", params={
|
||||
'key': 'toid',
|
||||
'id': toid
|
||||
})
|
||||
buildings = r.json()
|
||||
if buildings and len(buildings) == 1:
|
||||
bid = buildings[0]['building_id']
|
||||
print("match_by_toid", toid, bid)
|
||||
return bid
|
||||
|
||||
# try location
|
||||
poly = shape(geom)
|
||||
point_osgb = poly.centroid
|
||||
if not poly.contains(point_osgb):
|
||||
point_osgb = poly.representative_point()
|
||||
|
||||
point_ll = transform(osgb_to_ll, point_osgb)
|
||||
r = requests.get(base_url + "/buildings/locate", params={
|
||||
'lng': point_ll.x,
|
||||
'lat': point_ll.y
|
||||
})
|
||||
buildings = r.json()
|
||||
if buildings and len(buildings) == 1:
|
||||
bid = buildings[0]['building_id']
|
||||
print("match_by_location", toid, bid)
|
||||
return bid
|
||||
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
url, api_key, process, filename = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
||||
except IndexError:
|
||||
print(
|
||||
"Usage: {} <URL> <api_key> <camden|fitzrovia> ./path/to/camden.shp".format(
|
||||
os.path.basename(__file__)
|
||||
))
|
||||
exit()
|
||||
|
||||
main(url, api_key, process, filename)
|
@ -63,6 +63,9 @@ This is the main table, containing almost all data collected by Colouring London
|
||||
- `size_floor_area_ground`: ground floor floor area in square metres
|
||||
- `size_floor_area_total`: total floor area in square metres
|
||||
- `size_width_frontage`: width of frontage in metres
|
||||
- `construction_core_material`: main structural material
|
||||
- `construction_secondary_materials`: other structural materials
|
||||
- `construction_roof_covering`: main roof covering
|
||||
- `sust_breeam_rating`: BREEAM rating
|
||||
- `sust_dec`: DEC rating
|
||||
- `sust_retrofit_date`: year of last significant retrofit
|
||||
|
@ -36,6 +36,9 @@ COPY (SELECT
|
||||
sust_breeam_rating,
|
||||
sust_dec,
|
||||
sust_retrofit_date,
|
||||
construction_core_material,
|
||||
construction_secondary_materials,
|
||||
construction_roof_covering,
|
||||
planning_portal_link,
|
||||
planning_in_conservation_area,
|
||||
planning_conservation_area_name,
|
||||
|
35
migrations/011.sustainability.down1-extra.sql
Normal file
35
migrations/011.sustainability.down1-extra.sql
Normal file
@ -0,0 +1,35 @@
|
||||
-- Remove sustainability fields, update in paralell with adding new fields
|
||||
-- Last significant retrofit date YYYY
|
||||
-- Need to add a constraint to sust_retrofit_date
|
||||
-- Renewal technologies
|
||||
-- Constraint - Front end multi select back end ENUM
|
||||
-- Values:
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_renewables_tech;
|
||||
|
||||
--Has a building had a major renovation without extenstion (captured in form)
|
||||
--Boolean yes/no - links to the the DATE
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_retrofitted;
|
||||
|
||||
-- Generating capacity of those renewables, on selection of one of the above generate correspondening front end input for this. Pair values
|
||||
-- Constraint more than 0 less than?, integer only
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_renewables_capax;
|
||||
|
||||
-- Biodiversity
|
||||
-- Green roof, green wall, both
|
||||
-- Constrain drop down and enum
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_biodiversity;
|
||||
|
||||
-- Insulation, tool tip for glazing in construction to cross link
|
||||
-- Which components are insulated
|
||||
-- Cosntraint multi-entry and ENUM stored in josnb object
|
||||
-- Values; Wall, Roof, FLOOR
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_insulation;
|
||||
|
||||
-- Water recycling
|
||||
-- yes / no
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_h2o_recyling;
|
||||
|
||||
-- Rain water harvesting
|
||||
-- Does building store it's rainwater, helps combat flood risk
|
||||
-- yes / no
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_rainwater_harvest;
|
66
migrations/011.sustainability.up1-extra.sql
Normal file
66
migrations/011.sustainability.up1-extra.sql
Normal file
@ -0,0 +1,66 @@
|
||||
-- Remove sustainability fields, update in paralell with adding new fields
|
||||
-- Last significant retrofit date YYYY
|
||||
-- Need to add a constraint to sust_retrofit_date
|
||||
ALTER TABLE buildings
|
||||
ADD CONSTRAINT sust_retrofit_date_end CHECK (sust_retrofit_date <= DATE_PART('year', CURRENT_DATE));
|
||||
|
||||
--Has a building had a major renovation without extenstion (captured in form)
|
||||
--Boolean yes/no - links to the the DATE
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_retrofitted boolean DEFAULT 'n';
|
||||
|
||||
-- Renewal technologies
|
||||
-- Constraint - Front end multi select back end ENUM
|
||||
-- Values: Solar PV, Solar thermal, Wind, Ground sourced heat pump, Air sourced heat pump,
|
||||
CREATE TYPE sust_renewables_tech
|
||||
AS ENUM ('Solar photovoltaic',
|
||||
'Solar thermal',
|
||||
'Wind',
|
||||
'Ground source heat pump',
|
||||
'Air-source heat pump',
|
||||
'Water source heat pump',
|
||||
'Anaerobic digester');
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_renewables_tech sust_renewables_tech DEFAULT NULL;
|
||||
|
||||
-- Generating capacity of those renewables, on selection of one of the above generate correspondening front end input for this. Pair values
|
||||
-- Constraint more than 0 less than 9999
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_renewables_capax int CONSTRAINT high_renewables_capx CHECK (sust_renewables_capax >= 0);
|
||||
|
||||
-- Biodiversity
|
||||
-- Green roof, green wall, both
|
||||
-- Constrain drop down and enum
|
||||
CREATE TYPE sust_biodiversity
|
||||
AS ENUM ('Green roof',
|
||||
'Green wall',
|
||||
'Green wall & roof',
|
||||
'Anaerobic digester');
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_biodiversity sust_biodiversity DEFAULT NULL;
|
||||
|
||||
-- Insulation, tool tip for glazing in construction to cross link
|
||||
-- Which components are insulated
|
||||
-- Cosntraint multi-entry and ENUM stored in josnb object
|
||||
-- Values; Wall, Roof, FLOOR
|
||||
CREATE TYPE constrctn_insulation
|
||||
AS ENUM ('Cavity wall',
|
||||
'External wall',
|
||||
'Roof',
|
||||
'Floor');
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS constrctn_insulation constrctn_insulation DEFAULT NULL;
|
||||
|
||||
-- Water recycling
|
||||
-- yes / no
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_h2o_recyling boolean DEFAULT 'n';
|
||||
|
||||
-- Rain water harvesting
|
||||
-- Does building store it's rainwater, helps combat flood risk
|
||||
-- yes / no
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_rainwater_harvest boolean DEFAULT 'n';
|
13
migrations/012.community.down.sql
Normal file
13
migrations/012.community.down.sql
Normal file
@ -0,0 +1,13 @@
|
||||
-- Remove community fields
|
||||
|
||||
-- Ownership type, enumerate type from:
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS ownership_type;
|
||||
|
||||
-- Ownerhsip perception, would you describe this as a community asset?
|
||||
-- Boolean yes / no
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS ownership_perception;
|
||||
|
||||
-- Historic ownership type / perception
|
||||
-- Has this building ever been used for community or public services activities?
|
||||
-- Boolean yes / no
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS ownership_historic;
|
28
migrations/012.community.up.sql
Normal file
28
migrations/012.community.up.sql
Normal file
@ -0,0 +1,28 @@
|
||||
-- Remove community fields
|
||||
|
||||
-- Ownership type, enumerate type from:
|
||||
--
|
||||
|
||||
CREATE TYPE ownership_type
|
||||
AS ENUM ('Private individual',
|
||||
'Private company',
|
||||
'Private offshore ownership',
|
||||
'Publicly owned',
|
||||
'Institutionally owned');
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS ownership_type ownership_type DEFAULT 'Private individual';
|
||||
|
||||
-- Ownerhsip perception, would you describe this as a community asset?
|
||||
-- Boolean yes / no
|
||||
-- Below accepts t/f, yes/no, y/n, 0/1 as valid inputs all of which
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS ownership_perception boolean DEFAULT null;
|
||||
|
||||
-- Historic ownership type / perception
|
||||
-- Has this building ever been used for community or public services activities?
|
||||
-- Boolean yes / no
|
||||
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS ownership_historic boolean DEFAULT null;
|
@ -13,18 +13,3 @@ ALTER TABLE buildings DROP COLUMN IF EXISTS current_landuse_group;
|
||||
|
||||
-- Land use order, singular. Client and db constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS current_landuse_order;
|
||||
|
||||
|
||||
|
||||
--===========================================
|
||||
--
|
||||
-- We also collect original landuse, structure & process is as current land use
|
||||
-- We don't currently collect intermediate historic uses
|
||||
--
|
||||
--===========================================
|
||||
|
||||
-- Original Land use class or classes, array object, client constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_class;
|
||||
|
||||
-- Land use order, singular. Client and db constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_order;
|
||||
|
8
migrations/0xx.construction-materials.down.sql
Normal file
8
migrations/0xx.construction-materials.down.sql
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS core_materials;
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS secondary_materials;
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS roof_covering;
|
||||
|
||||
|
||||
DROP TYPE IF EXISTS construction_materials;
|
||||
DROP TYPE IF EXISTS roof_covering;
|
38
migrations/0xx.construction-materials.up.sql
Normal file
38
migrations/0xx.construction-materials.up.sql
Normal file
@ -0,0 +1,38 @@
|
||||
-- Launch of Constuction category
|
||||
-- Fields: Core construction material, Secondary construction materials, Roof covering materials
|
||||
|
||||
-- Construction materials: Wood, Stone, Brick, Steel, Reinforced Concrete, Other Metal
|
||||
-- Other Natural Material, Other Man-Made Material
|
||||
CREATE TYPE construction_materials
|
||||
AS ENUM ('Wood',
|
||||
'Stone',
|
||||
'Brick',
|
||||
'Steel',
|
||||
'Reinforced Concrete',
|
||||
'Other Metal',
|
||||
'Other Natural Material',
|
||||
'Other Man-Made Material');
|
||||
|
||||
-- Roof covering materials: Slate, Clay Tile, Wood, Asphalt, Iron or Steel, Other Metal
|
||||
-- Other Natural Material, Other Man-Made Material
|
||||
CREATE TYPE roof_covering
|
||||
AS ENUM ('Slate',
|
||||
'Clay Tile',
|
||||
'Wood',
|
||||
'Asphalt',
|
||||
'Iron or Steel',
|
||||
'Other Metal',
|
||||
'Other Natural Material',
|
||||
'Other Man-Made Material');
|
||||
|
||||
-- Core Construction Material
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS core_materials construction_materials;
|
||||
|
||||
-- Secondary Construction Materials
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS secondary_materials construction_materials;
|
||||
|
||||
-- Main Roof Covering
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS roof_covering roof_covering;
|
23
migrations/0xx.construction.down.sql
Normal file
23
migrations/0xx.construction.down.sql
Normal file
@ -0,0 +1,23 @@
|
||||
--NOTE Some construction category fields (insulation and glazing) are migrated in sustainability.up.1-extra see #405
|
||||
--Primary material (brick/clay tile, slate, steel/metal, timber, stone, glass, concrete, natural-green or thatch, asphalt or other)
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_primary_mat;
|
||||
|
||||
--Secondary material
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_secondary_mat;
|
||||
|
||||
--Primary roof material
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_primary_roof_mat;
|
||||
|
||||
--Secondary roof material
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_secondary_roof_mat;
|
||||
|
||||
--Has building been extended y/n
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_extension;
|
||||
|
||||
--What kind of extensions (side, rear, loft, basement)
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_extension_type;
|
||||
|
||||
--Glazing type, what is most common glazing type. (unless I add into sustainability for now)
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS constrctn_glazing_type;
|
||||
|
||||
--Also pick up roof in this for
|
3
migrations/0xx.team.down.sql
Normal file
3
migrations/0xx.team.down.sql
Normal file
@ -0,0 +1,3 @@
|
||||
-- Remove team fields, update in paralell with adding new fields
|
||||
-- Award or awards (may be multiple) stored as json b object
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS team_awards;
|
8
migrations/0xx.team.up.sql
Normal file
8
migrations/0xx.team.up.sql
Normal file
@ -0,0 +1,8 @@
|
||||
--Storing as json b, create column defined as jsonb
|
||||
--This contains the award name and award year
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS team_awards jsonb;
|
||||
|
||||
--To validate this input, the following confirms it's an valid object but not that the items in the object are validated agains those we will acccept
|
||||
ALTER TABLE buildings
|
||||
ADD CONSTRAINT data_is_valid CHECK (is_jsonb_valid ('{"type": "object"}', team_awards));
|
16
migrations/0xx.type-extend.down.sql
Normal file
16
migrations/0xx.type-extend.down.sql
Normal file
@ -0,0 +1,16 @@
|
||||
-- Building original use, collecting both class and order
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_class;
|
||||
|
||||
-- [Disabled for launch] Date of change of use
|
||||
-- This needs to pair with demolition
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS date_change_building_use;
|
||||
|
||||
|
||||
-- Original Land use class or classes, array object, client constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_class;
|
||||
|
||||
-- Land use order, singular. Client and db constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_order;
|
||||
|
||||
-- Land use order, singular. Client and db constrained.
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS original_landuse_source;
|
14
migrations/0xx.type-extend.up.sql
Normal file
14
migrations/0xx.type-extend.up.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- Land use order, singular. Client and db constrained with foreign key
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS original_landuse_order text,
|
||||
ADD FOREIGN KEY (current_landuse_order)
|
||||
REFERENCES reference_tables.buildings_landuse_order (description);
|
||||
|
||||
-- Land use groups, array. Derived from classes.
|
||||
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS original_landuse_group text ARRAY[41];
|
||||
|
||||
-- Land use class or classes, array object, client constrained. ARRAY[] is used to constrain array size. The array is limited to 250 based on Westfield Stratford as a single toid with many uses, this may want to be reduced down to reduce maximum size.
|
||||
|
||||
ALTER TABLE buildings ADD COLUMN IF NOT EXISTS original_landuse_class text ARRAY[250];
|
||||
|
||||
--Landuse source
|
@ -53,3 +53,17 @@ Set or update passwords:
|
||||
```bash
|
||||
psql -c "ALTER USER appusername WITH PASSWORD 'longsecurerandompassword';"
|
||||
```
|
||||
|
||||
## File naming syntax
|
||||
|
||||
Initial up and down migrations as `###.name.up.sql` file number should be sequential
|
||||
and incremental to last migrations file number is same for up/down.
|
||||
|
||||
If adjusting a prior migration syntax is:
|
||||
|
||||
###.name.up.sql
|
||||
|
||||
Syntax for adding to existing migration:
|
||||
|
||||
0##.filename-extension-#.up.sql
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user