Skip to content
Snippets Groups Projects
Commit ac18d96a authored by Bjarke Madsen's avatar Bjarke Madsen
Browse files

Add a way to exclude publishing data for an NREN for a given year.

NRENs cannot answer surveys if they have any exclusions.
parent f15d2383
Branches
Tags
No related merge requests found
import { ReactElement, useContext, useState, useEffect } from "react";
import { useNavigate, Link } from "react-router-dom";
import { Table, Container, Row } from "react-bootstrap";
import * as XLSX from "xlsx";
import SurveySidebar from "compendium/survey/management/SurveySidebar";
import useMatomo from "compendium/matomo/UseMatomo";
import { userContext } from "compendium/providers/UserProvider";
import { fetchSurveys, fetchActiveSurveyYear } from "compendium/survey/api/survey";
import { Survey } from "compendium/survey/api/types";
import SurveySidebar from "compendium/survey/management/SurveySidebar";
import * as XLSX from "xlsx";
import useMatomo from "compendium/matomo/UseMatomo";
interface NRENExclusion {
nren: string;
year: number;
}
const NrenExclusionsTable = () => {
const [nrenExclusions, setNrenExclusions] = useState<NRENExclusion[]>([]);
const [nrens, setNrens] = useState<string[]>([]);
function fetchNrenExclusions() {
fetch('/api/nren-exclusions')
.then(response => response.json())
.then(data => {
setNrenExclusions(data);
})
.catch(error => {
console.error('Error fetching NREN exclusions:', error);
});
}
useEffect(() => {
fetch('/api/nren/list')
.then(response => response.json())
.then(data => {
const nrenNames = data.map(nren => nren.name);
setNrens(nrenNames);
})
.catch(error => {
console.error('Error fetching NRENs:', error);
});
}, []);
async function addNrenExclusion(nren: string, year: number) {
const response = await fetch('/api/nren-exclusions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ nren, year }),
});
const json = await response.json();
if (response.ok) {
setNrenExclusions(json);
} else {
alert(json.message)
}
(document.getElementById('excludenren') as HTMLInputElement).value = '';
(document.getElementById('excludeyear') as HTMLInputElement).value = '';
}
async function deleteNrenExclusion(nren: string, year: number) {
const response = await fetch(`/api/nren-exclusions`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ nren, year }),
});
const json = await response.json();
if (response.ok) {
setNrenExclusions(json);
} else {
alert(json.message)
}
}
useEffect(() => {
fetchNrenExclusions();
}, []);
return (
<>
<hr className="fake-divider" />
<h2>NREN Exclusions</h2>
<p>Use this table to exclude data for a specific NREN and year.</p>
<p>If an NREN shows up in this table, they will not be allowed to view/edit or answer their survey.</p>
<Table striped bordered responsive>
<thead>
<tr>
<th>NREN</th>
<th>Year</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<select id="excludenren">
<option value="">Select NREN</option>
{nrens.map(nren => (
<option key={nren} value={nren}>{nren}</option>
))}
</select>
</td>
<td>
<input id="excludeyear" type="year" name="year" />
</td>
<td>
<button className="btn btn-primary" onClick={() => {
const nren = (document.getElementById('excludenren') as HTMLInputElement).value;
const year = parseInt((document.getElementById('excludeyear') as HTMLInputElement).value);
if (!nren || !year) {
alert('Please enter an NREN and year');
return;
}
addNrenExclusion(nren, year);
}}>Add Exclusion</button>
</td>
</tr>
{nrenExclusions.map(exclusion => (
<tr key={exclusion.nren + exclusion.year}>
<td>{exclusion.nren}</td>
<td>{exclusion.year}</td>
<td>
<button className="btn btn-danger" onClick={
() => deleteNrenExclusion(exclusion.nren, exclusion.year)
}>Delete</button>
</td>
</tr>
))}
</tbody>
</Table>
<hr className="fake-divider" />
</>
);
}
const SurveyTable = () => {
......@@ -186,6 +318,7 @@ function Landing(): ReactElement {
{loggedIn && !isObserver && !hasNren && <li><span>Once you have been approved, you will immediately be directed to the relevant survey upon visiting this page</span></li>}
{loggedIn && isObserver && <li><span>You have read-only access to the following surveys:</span></li>}
</ul>}
{isAdmin && <NrenExclusionsTable />}
{loggedIn && isObserver && <SurveyTable />}
</div>
</div>
......
......@@ -67,6 +67,20 @@ class NREN(PresentationModel):
name: Mapped[str256]
country: Mapped[str256]
exclusions = relationship('NRENExclusion', lazy='selectin')
def can_answer_survey(self):
"""
Some NRENs are excluded from the survey and data for certain years will not be published.
If any exclusions for an NREN are found, future surveys should not be created for that NREN.
Each exclusion is a year, for which the responses for that year are not published.
"""
return not self.exclusions
def can_publish_response(self, year):
return not any(exclusion.year == year for exclusion in self.exclusions)
def to_dict(self, download=False):
return {
'nren': self.name,
......@@ -90,6 +104,20 @@ class NREN(PresentationModel):
return hasattr(other, 'id') and self.id == other.id
class NRENExclusion(PresentationModel):
__tablename__ = 'nren_exclusion'
nren_id: Mapped[int_pk_fkNREN]
year: Mapped[int_pk]
nren = relationship(NREN, lazy='joined', back_populates='exclusions')
def to_dict(self, download=False):
return {
'nren': self.nren.name,
'year': self.year,
}
class BudgetEntry(PresentationModel):
"""
The BudgetEntry model represents the budget of an NREN for a specific year.
......
......@@ -9,7 +9,8 @@ from typing import Type, Sequence, TypeVar, Dict, Any
from itertools import chain
from compendium_v2.db import db
from compendium_v2.db.presentation_models import NREN, PreviewYear, PresentationModel, ExternalConnections
from compendium_v2.db.presentation_models import (
NREN, NRENExclusion, PreviewYear, PresentationModel, ExternalConnections)
from sqlalchemy import select
T = TypeVar('T', bound=PresentationModel)
......@@ -19,7 +20,9 @@ def get_data(table_class: Type[T]) -> Sequence[T]:
if table_class == NREN:
data = [extract_model_data(n) for n in db.session.scalars(select(NREN).order_by(NREN.name.asc()))]
return list(chain(*data))
select_statement = select(table_class).join(NREN).order_by(NREN.name.asc(), table_class.year.asc())
select_statement = select(table_class).join(NREN).order_by(NREN.name.asc(), table_class.year.asc()).where(
~table_class.nren_id.in_(select(NRENExclusion.nren_id).where(NRENExclusion.year == table_class.year))
)
can_preview = (not current_user.is_anonymous) and current_user.can_preview
preview = can_preview and request.args.get('preview') is not None
......
"""Add NREN exclusions
Revision ID: c49b3a8b3c30
Revises: 7d1a8783770d
Create Date: 2025-02-12 16:24:27.204181
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c49b3a8b3c30'
down_revision = '7d1a8783770d'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'nren_exclusion',
sa.Column('nren_id', sa.Integer(), nullable=False),
sa.Column('year', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['nren_id'], ['nren.id'], name=op.f('fk_nren_exclusion_nren_id_nren')),
sa.PrimaryKeyConstraint('nren_id', 'year', name=op.f('pk_nren_exclusion'))
)
def downgrade():
op.drop_table('nren_exclusion')
......@@ -23,6 +23,7 @@ from compendium_v2.db.survey_models import ResponseStatus, SurveyResponse
from compendium_v2.publishers.year.map_2023 import map_2023
from compendium_v2.publishers.year.map_2024 import map_2024
from compendium_v2.db.presentation_models import (
NRENExclusion,
PresentationModel,
AlienWave,
BudgetEntry,
......@@ -294,8 +295,12 @@ def save_data(year: int, data: Dict[PresentationModel, Sequence[Dict[str, Any]]]
def publish(year, dry_run=False):
responses = db.session.scalars(
select(SurveyResponse).where(SurveyResponse.survey_year == year)
.where(SurveyResponse.status == ResponseStatus.completed)
select(SurveyResponse).where(
SurveyResponse.survey_year == year, SurveyResponse.status == ResponseStatus.completed,
SurveyResponse.valid == True).where(~SurveyResponse.nren_id.in_( # noqa: E712
select(NRENExclusion.nren_id).where(NRENExclusion.year == year)
)
)
).unique().all()
question_mapping = {
......
import logging
from flask import Blueprint, jsonify
from flask import Blueprint, jsonify, request
from sqlalchemy import func
from compendium_v2.auth.session_management import admin_required
from compendium_v2.db import db
from compendium_v2.helpers import get_data
from compendium_v2.routes import common
from compendium_v2.routes.survey import routes as survey
......@@ -7,6 +10,7 @@ from compendium_v2.routes.user import routes as user_routes
from compendium_v2.routes.response import routes as response_routes
from compendium_v2.routes.data_download import routes as data_download
from compendium_v2.db.presentation_models import (
NRENExclusion,
BudgetEntry, FundingSource, ChargingStructure, NrenStaff, ParentOrganization, SubOrganization,
ECProject, Policy, TrafficVolume, InstitutionURLs, CentralProcurement, ServiceManagement,
ServiceUserTypes, EOSCListings, Standards, CrisisExercises, SecurityControls, ConnectedProportion,
......@@ -75,6 +79,48 @@ models = [
]
@routes.route('/nren-exclusions', methods=['GET', 'POST', 'DELETE'])
@admin_required
@common.require_accepts_json
def nren_exclusions():
if request.method == 'POST':
data = request.get_json()
nren_name = data.get('nren')
year = data.get('year')
nren = db.session.query(NREN).filter(func.lower(NREN.name) == nren_name.lower()).first()
if not nren:
return jsonify({'message': f'NREN {nren_name} not found'}), 400
exists = next((exclusion for exclusion in nren.exclusions if exclusion.year == year), None)
if exists:
return jsonify({'message': f'Exclusion for {nren_name} in {year} already exists'}), 400
exclusion = NRENExclusion(nren=nren, year=year)
db.session.add(exclusion)
db.session.commit()
elif request.method == 'DELETE':
data = request.get_json()
nren_name = data.get('nren')
year = data.get('year')
nren = db.session.query(NREN).filter(func.lower(NREN.name) == nren_name.lower()).first()
if not nren:
return jsonify({'message': f'NREN {nren_name} not found'}), 400
exclusion = next((exclusion for exclusion in nren.exclusions if exclusion.year == year), None)
if not exclusion:
return jsonify({'message': f'Exclusion for {nren_name} in {year} not found'}), 400
db.session.delete(exclusion)
db.session.commit()
exclusions = db.session.query(NRENExclusion).all()
return jsonify([exclusion.to_dict() for exclusion in exclusions])
def create_view_func(model):
return lambda: jsonify(get_data(model))
......
......@@ -78,11 +78,13 @@ class SurveyMode(str, Enum):
Edit = "edit"
def check_access_nren_read(user: User, nren: str) -> bool:
def check_access_nren_read(user: User, nren: NREN) -> bool:
if user.is_anonymous:
return False
if user.is_admin:
return True
if not nren.can_answer_survey():
return False
if user.is_observer:
return True
if nren == user.nren:
......@@ -90,7 +92,7 @@ def check_access_nren_read(user: User, nren: str) -> bool:
return False
def check_access_nren_write(user: User, nren: str) -> bool:
def check_access_nren_write(user: User, nren: NREN) -> bool:
if not check_access_nren_read(user, nren):
# if you can't read it, you definitely shouldn't write to it
return False
......
......@@ -147,7 +147,7 @@ def list_surveys() -> Any:
return response.status.value + response.nren.name.lower()
all_nrens = db.session.scalars(select(NREN)).all()
nrens = {nren.name: nren.id for nren in all_nrens}
nrens = {nren.name: nren for nren in all_nrens}
nren_names = set(nrens.keys())
all_surveys: List[SurveyDict] = []
......@@ -166,13 +166,17 @@ def list_surveys() -> Any:
nrens_with_responses = set([r["nren"]["name"] for r in survey_dict["responses"]])
missing_responses = nren_names.difference(nrens_with_responses)
for nren_name in sorted(missing_responses, key=str.lower):
nren = nrens[nren_name]
if not nren.can_answer_survey():
# exclude nrens that are not allowed to answer the survey
continue
responses.append({
"status": RESPONSE_NOT_STARTED if status == SurveyStatus.open else RESPONSE_NOT_COMPLETED,
"lock_description": "",
"valid": False,
"nren": {
'name': nren_name,
'id': nrens[nren_name]
'id': nren.id
},
})
......
import{c as Q,Q as K,r as L,a1 as X,D as Z,j as t,L as z,E as ee,R as te}from"./index.js";import{u as W,w as ne}from"./xlsx-BHRztzV8.js";import{S as re}from"./SurveySidebar-CG0gwQ6b.js";import{f as se,a as le}from"./survey-3meXCY6T.js";import{T as q}from"./Table-ClWM2_rS.js";import"./SideBar-CkoMfgfL.js";const oe=()=>{const e=Q.c(32);let i;e[0]===Symbol.for("react.memo_cache_sentinel")?(i=[],e[0]=i):i=e[0];const[u,m]=L.useState(i);let n;e[1]===Symbol.for("react.memo_cache_sentinel")?(n=[],e[1]=n):n=e[1];const[a,h]=L.useState(n);let s;e[2]===Symbol.for("react.memo_cache_sentinel")?(s=function(){fetch("/api/nren-exclusions").then(de).then(r=>{m(r)}).catch(me)},e[2]=s):s=e[2];const c=s;let f,P;e[3]===Symbol.for("react.memo_cache_sentinel")?(f=()=>{fetch("/api/nren/list").then(he).then(l=>{const r=l.map(ue);h(r)}).catch(fe)},P=[],e[3]=f,e[4]=P):(f=e[3],P=e[4]),L.useEffect(f,P);let E;e[5]===Symbol.for("react.memo_cache_sentinel")?(E=async function(r,D){const N=await fetch("/api/nren-exclusions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({nren:r,year:D})}),$=await N.json();N.ok?m($):alert($.message),document.getElementById("excludenren").value="",document.getElementById("excludeyear").value=""},e[5]=E):E=e[5];const G=E;let S;e[6]===Symbol.for("react.memo_cache_sentinel")?(S=async function(r,D){const N=await fetch("/api/nren-exclusions",{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({nren:r,year:D})}),$=await N.json();N.ok?m($):alert($.message)},e[6]=S):S=e[6];const H=S;let w,M;e[7]===Symbol.for("react.memo_cache_sentinel")?(w=()=>{c()},M=[],e[7]=w,e[8]=M):(w=e[7],M=e[8]),L.useEffect(w,M);let x,C,k,R;e[9]===Symbol.for("react.memo_cache_sentinel")?(R=t.jsx("hr",{className:"fake-divider"}),x=t.jsx("h2",{children:"NREN Exclusions"}),C=t.jsx("p",{children:"Use this table to exclude data for a specific NREN and year."}),k=t.jsx("p",{children:"If an NREN shows up in this table, they will not be allowed to view/edit or answer their survey."}),e[9]=x,e[10]=C,e[11]=k,e[12]=R):(x=e[9],C=e[10],k=e[11],R=e[12]);let T;e[13]===Symbol.for("react.memo_cache_sentinel")?(T=t.jsx("thead",{children:t.jsxs("tr",{children:[t.jsx("th",{children:"NREN"}),t.jsx("th",{children:"Year"}),t.jsx("th",{children:"Actions"})]})}),e[13]=T):T=e[13];let A;e[14]===Symbol.for("react.memo_cache_sentinel")?(A=t.jsx("option",{value:"",children:"Select NREN"}),e[14]=A):A=e[14];let y;e[15]!==a?(y=a.map(xe),e[15]=a,e[16]=y):y=e[16];let p;e[17]!==y?(p=t.jsx("td",{children:t.jsxs("select",{id:"excludenren",children:[A,y]})}),e[17]=y,e[18]=p):p=e[18];let O;e[19]===Symbol.for("react.memo_cache_sentinel")?(O=t.jsx("td",{children:t.jsx("input",{id:"excludeyear",type:"year",name:"year"})}),e[19]=O):O=e[19];let I;e[20]===Symbol.for("react.memo_cache_sentinel")?(I=t.jsx("td",{children:t.jsx("button",{className:"btn btn-primary",onClick:()=>{const l=document.getElementById("excludenren").value,r=parseInt(document.getElementById("excludeyear").value);if(!l||!r){alert("Please enter an NREN and year");return}G(l,r)},children:"Add Exclusion"})}),e[20]=I):I=e[20];let j;e[21]!==p?(j=t.jsxs("tr",{children:[p,O,I]}),e[21]=p,e[22]=j):j=e[22];let b;if(e[23]!==u){let l;e[25]===Symbol.for("react.memo_cache_sentinel")?(l=r=>t.jsxs("tr",{children:[t.jsx("td",{children:r.nren}),t.jsx("td",{children:r.year}),t.jsx("td",{children:t.jsx("button",{className:"btn btn-danger",onClick:()=>H(r.nren,r.year),children:"Delete"})})]},r.nren+r.year),e[25]=l):l=e[25],b=u.map(l),e[23]=u,e[24]=b}else b=e[24];let d;e[26]!==j||e[27]!==b?(d=t.jsxs(q,{striped:!0,bordered:!0,responsive:!0,children:[T,t.jsxs("tbody",{children:[j,b]})]}),e[26]=j,e[27]=b,e[28]=d):d=e[28];let _;e[29]===Symbol.for("react.memo_cache_sentinel")?(_=t.jsx("hr",{className:"fake-divider"}),e[29]=_):_=e[29];let v;return e[30]!==d?(v=t.jsxs(t.Fragment,{children:[R,x,C,k,d,_]}),e[30]=d,e[31]=v):v=e[31],v},ce=()=>{const e=Q.c(7),[i,u]=L.useState();let m,n;e[0]===Symbol.for("react.memo_cache_sentinel")?(m=()=>{le().then(c=>{u(c[0])})},n=[],e[0]=m,e[1]=n):(m=e[0],n=e[1]),L.useEffect(m,n);let a;e[2]===Symbol.for("react.memo_cache_sentinel")?(a=t.jsx("thead",{children:t.jsxs("tr",{children:[t.jsx("th",{children:"(N)REN"}),t.jsx("th",{children:"Link"}),t.jsx("th",{children:"Survey Status"})]})}),e[2]=a):a=e[2];let h;e[3]!==i?(h=i&&i.responses.map(c=>t.jsxs("tr",{children:[t.jsx("td",{children:c.nren.name}),t.jsx("td",{children:t.jsx(z,{to:`/survey/response/${i.year}/${c.nren.name}`,children:t.jsx("span",{children:"Navigate to survey"})})}),t.jsx("td",{children:c.status})]},c.nren.id)),e[3]=i,e[4]=h):h=e[4];let s;return e[5]!==h?(s=t.jsxs(q,{striped:!0,bordered:!0,responsive:!0,children:[a,t.jsx("tbody",{children:h})]}),e[5]=h,e[6]=s):s=e[6],s};function ge(){const e=Q.c(40),{trackPageView:i}=K(),{user:u}=L.useContext(X),m=Z(),n=!!u.id,a=n?!!u.nrens.length:!1,h=a?u.nrens[0]:"",s=n?u.permissions.admin:!1,c=n?u.role==="observer":!1,[f,P]=L.useState(null);let E,G;e[0]!==i?(E=()=>{(async()=>{const N=await se();P(N)})(),i({documentTitle:"GEANT Survey Landing Page"})},G=[i],e[0]=i,e[1]=E,e[2]=G):(E=e[1],G=e[2]),L.useEffect(E,G);let S;e[3]!==h||e[4]!==f||e[5]!==m?(S=()=>{try{return m(`/survey/response/${f}/${h}`),t.jsx("li",{children:"Redirecting to survey..."})}catch(D){return console.error("Error navigating:",D),null}},e[3]=h,e[4]=f,e[5]=m,e[6]=S):S=e[6];const H=S;let w;if(e[7]===Symbol.for("react.memo_cache_sentinel")){const D=function(Y,F,g){const B=W.decode_range(Y["!ref"]??"");let J=-1;for(let o=B.s.c;o<=B.e.c;o++){const U=W.encode_cell({r:B.s.r,c:o}),V=Y[U];if(V&&typeof V.v=="string"&&V.v===F){J=o;break}}if(J===-1){console.error(`Column '${F}' not found.`);return}for(let o=B.s.r+1;o<=B.e.r;++o){const U=W.encode_cell({r:o,c:J});Y[U]&&Y[U].t==="n"&&(Y[U].z=g)}},N=function(Y){const F=W.book_new();Y.forEach(o=>{const U=W.json_to_sheet(o.data);o.meta&&D(U,o.meta.columnName,o.meta.format),W.book_append_sheet(F,U,o.name)});const g=ne(F,{bookType:"xlsx",type:"binary"}),B=new ArrayBuffer(g.length),J=new Uint8Array(B);for(let o=0;o<g.length;o++)J[o]=g.charCodeAt(o)&255;return new Blob([B],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"})};w=function(){fetch("/api/data-download").then(ae).then(Y=>{const F=N(Y),g=document.createElement("a");g.href=URL.createObjectURL(F),g.download="data.xlsx",document.body.appendChild(g),g.click(),document.body.removeChild(g)}).catch(ie)},e[7]=w}else w=e[7];const M=w;let x;e[8]!==s?(x=s&&t.jsx(re,{}),e[8]=s,e[9]=x):x=e[9];let C;e[10]===Symbol.for("react.memo_cache_sentinel")?(C=t.jsx("h1",{className:"geant-header",children:"THE GÉANT COMPENDIUM OF NRENS SURVEY"}),e[10]=C):C=e[10];let k,R;e[11]===Symbol.for("react.memo_cache_sentinel")?(k={maxWidth:"75rem"},R={textAlign:"left"},e[11]=k,e[12]=R):(k=e[11],R=e[12]);let T;e[13]===Symbol.for("react.memo_cache_sentinel")?(T=t.jsx("br",{}),e[13]=T):T=e[13];let A;e[14]===Symbol.for("react.memo_cache_sentinel")?(A=t.jsx("a",{href:"/login",children:"here"}),e[14]=A):A=e[14];let y;e[15]===Symbol.for("react.memo_cache_sentinel")?(y=t.jsx("br",{}),e[15]=y):y=e[15];let p;e[16]===Symbol.for("react.memo_cache_sentinel")?(p=t.jsx("br",{}),e[16]=p):p=e[16];let O,I,j,b;e[17]===Symbol.for("react.memo_cache_sentinel")?(O=t.jsxs("p",{style:R,children:["Hello,",T,"Welcome to the GÉANT Compendium Survey. (N)REN Compendium administrators can login via Single Sign On (SSO) ",A,", which will complete their registration to fill in the latest Compendium survey. This will send a notification to the Compendium administration team and they will assign you to your (N)REN.",y,"Once this step has been completed, you will receive an email from the administration team. We aim to get back to you the same working day, but sometimes may take a little longer.",p,"If you are not sure whether you are a Compendium Administrator for your (N)REN, please contact your GÉANT Partner Relations relationship manager.",t.jsx("br",{}),"Thank you."]}),I=t.jsx("span",{children:"Current registration status:"}),j=t.jsx("br",{}),b=t.jsx("br",{}),e[17]=O,e[18]=I,e[19]=j,e[20]=b):(O=e[17],I=e[18],j=e[19],b=e[20]);let d;e[21]!==f||e[22]!==a||e[23]!==s||e[24]!==c||e[25]!==n||e[26]!==H?(d=s?t.jsxs("ul",{children:[t.jsx("li",{children:t.jsx("span",{children:"You are logged in as a Compendium Administrator"})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx(z,{to:"/survey/admin/surveys",children:"here"})," to access the survey management page."]})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx(z,{to:"/survey/admin/users",children:"here"})," to access the user management page."]})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx("a",{href:"#",onClick:M,children:"here"})," to do the full data download."]})})]}):t.jsxs("ul",{children:[f&&!s&&!c&&a&&H(),n?t.jsx("li",{children:t.jsx("span",{children:"You are logged in"})}):t.jsx("li",{children:t.jsx("span",{children:"You are not logged in"})}),n&&!c&&!a&&t.jsx("li",{children:t.jsx("span",{children:"Your access to the survey has not yet been approved"})}),n&&!c&&!a&&t.jsx("li",{children:t.jsx("span",{children:"Once you have been approved, you will immediately be directed to the relevant survey upon visiting this page"})}),n&&c&&t.jsx("li",{children:t.jsx("span",{children:"You have read-only access to the following surveys:"})})]}),e[21]=f,e[22]=a,e[23]=s,e[24]=c,e[25]=n,e[26]=H,e[27]=d):d=e[27];let _;e[28]!==s?(_=s&&t.jsx(oe,{}),e[28]=s,e[29]=_):_=e[29];let v;e[30]!==c||e[31]!==n?(v=n&&c&&t.jsx(ce,{}),e[30]=c,e[31]=n,e[32]=v):v=e[32];let l;e[33]!==d||e[34]!==_||e[35]!==v?(l=t.jsx(ee,{className:"py-5 grey-container",children:t.jsx(te,{children:t.jsxs("div",{className:"center-text",children:[C,t.jsxs("div",{className:"wordwrap pt-4",style:k,children:[O,I,j,b,d,_,v]})]})})}),e[33]=d,e[34]=_,e[35]=v,e[36]=l):l=e[36];let r;return e[37]!==l||e[38]!==x?(r=t.jsxs(t.Fragment,{children:[x,l]}),e[37]=l,e[38]=x,e[39]=r):r=e[39],r}function ie(e){console.error("Error fetching data:",e),alert("An error occurred while creating the data download Excel file.")}function ae(e){if(!e.ok)throw new Error("Network response was not ok");return e.json()}function de(e){return e.json()}function me(e){console.error("Error fetching NREN exclusions:",e)}function he(e){return e.json()}function ue(e){return e.name}function fe(e){console.error("Error fetching NRENs:",e)}function xe(e){return t.jsx("option",{value:e,children:e},e)}export{ge as default};
import{c as V,Q as q,r as _,a1 as J,D as K,j as t,L as W,E as X,R as Z}from"./index.js";import{f as ee,a as te}from"./survey-3meXCY6T.js";import{S as ne}from"./SurveySidebar-CG0gwQ6b.js";import{u as b,w as re}from"./xlsx-BHRztzV8.js";import{T as se}from"./Table-ClWM2_rS.js";import"./SideBar-CkoMfgfL.js";const oe=()=>{const e=V.c(7),[i,f]=_.useState();let h,r;e[0]===Symbol.for("react.memo_cache_sentinel")?(h=()=>{te().then(s=>{f(s[0])})},r=[],e[0]=h,e[1]=r):(h=e[0],r=e[1]),_.useEffect(h,r);let l;e[2]===Symbol.for("react.memo_cache_sentinel")?(l=t.jsx("thead",{children:t.jsxs("tr",{children:[t.jsx("th",{children:"(N)REN"}),t.jsx("th",{children:"Link"}),t.jsx("th",{children:"Survey Status"})]})}),e[2]=l):l=e[2];let c;e[3]!==i?(c=i&&i.responses.map(s=>t.jsxs("tr",{children:[t.jsx("td",{children:s.nren.name}),t.jsx("td",{children:t.jsx(W,{to:`/survey/response/${i.year}/${s.nren.name}`,children:t.jsx("span",{children:"Navigate to survey"})})}),t.jsx("td",{children:s.status})]},s.nren.id)),e[3]=i,e[4]=c):c=e[4];let o;return e[5]!==c?(o=t.jsxs(se,{striped:!0,bordered:!0,responsive:!0,children:[l,t.jsx("tbody",{children:c})]}),e[5]=c,e[6]=o):o=e[6],o};function fe(){const e=V.c(37),{trackPageView:i}=q(),{user:f}=_.useContext(J),h=K(),r=!!f.id,l=r?!!f.nrens.length:!1,c=l?f.nrens[0]:"",o=r?f.permissions.admin:!1,s=r?f.role==="observer":!1,[y,z]=_.useState(null);let w,E;e[0]!==i?(w=()=>{(async()=>{const P=await ee();z(P)})(),i({documentTitle:"GEANT Survey Landing Page"})},E=[i],e[0]=i,e[1]=w,e[2]=E):(w=e[1],E=e[2]),_.useEffect(w,E);let N;e[3]!==c||e[4]!==y||e[5]!==h?(N=()=>{try{return h(`/survey/response/${y}/${c}`),t.jsx("li",{children:"Redirecting to survey..."})}catch(I){return console.error("Error navigating:",I),null}},e[3]=c,e[4]=y,e[5]=h,e[6]=N):N=e[6];const B=N;let C;if(e[7]===Symbol.for("react.memo_cache_sentinel")){const I=function(d,x,a){const m=b.decode_range(d["!ref"]??"");let S=-1;for(let n=m.s.c;n<=m.e.c;n++){const u=b.encode_cell({r:m.s.r,c:n}),M=d[u];if(M&&typeof M.v=="string"&&M.v===x){S=n;break}}if(S===-1){console.error(`Column '${x}' not found.`);return}for(let n=m.s.r+1;n<=m.e.r;++n){const u=b.encode_cell({r:n,c:S});d[u]&&d[u].t==="n"&&(d[u].z=a)}},P=function(d){const x=b.book_new();d.forEach(n=>{const u=b.json_to_sheet(n.data);n.meta&&I(u,n.meta.columnName,n.meta.format),b.book_append_sheet(x,u,n.name)});const a=re(x,{bookType:"xlsx",type:"binary"}),m=new ArrayBuffer(a.length),S=new Uint8Array(m);for(let n=0;n<a.length;n++)S[n]=a.charCodeAt(n)&255;return new Blob([m],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"})};C=function(){fetch("/api/data-download").then(ae).then(d=>{const x=P(d),a=document.createElement("a");a.href=URL.createObjectURL(x),a.download="data.xlsx",document.body.appendChild(a),a.click(),document.body.removeChild(a)}).catch(le)},e[7]=C}else C=e[7];const Q=C;let p;e[8]!==o?(p=o&&t.jsx(ne,{}),e[8]=o,e[9]=p):p=e[9];let k;e[10]===Symbol.for("react.memo_cache_sentinel")?(k=t.jsx("h1",{className:"geant-header",children:"THE GÉANT COMPENDIUM OF NRENS SURVEY"}),e[10]=k):k=e[10];let T,A;e[11]===Symbol.for("react.memo_cache_sentinel")?(T={maxWidth:"75rem"},A={textAlign:"left"},e[11]=T,e[12]=A):(T=e[11],A=e[12]);let R;e[13]===Symbol.for("react.memo_cache_sentinel")?(R=t.jsx("br",{}),e[13]=R):R=e[13];let Y;e[14]===Symbol.for("react.memo_cache_sentinel")?(Y=t.jsx("a",{href:"/login",children:"here"}),e[14]=Y):Y=e[14];let L;e[15]===Symbol.for("react.memo_cache_sentinel")?(L=t.jsx("br",{}),e[15]=L):L=e[15];let O;e[16]===Symbol.for("react.memo_cache_sentinel")?(O=t.jsx("br",{}),e[16]=O):O=e[16];let $,D,U,F;e[17]===Symbol.for("react.memo_cache_sentinel")?($=t.jsxs("p",{style:A,children:["Hello,",R,"Welcome to the GÉANT Compendium Survey. (N)REN Compendium administrators can login via Single Sign On (SSO) ",Y,", which will complete their registration to fill in the latest Compendium survey. This will send a notification to the Compendium administration team and they will assign you to your (N)REN.",L,"Once this step has been completed, you will receive an email from the administration team. We aim to get back to you the same working day, but sometimes may take a little longer.",O,"If you are not sure whether you are a Compendium Administrator for your (N)REN, please contact your GÉANT Partner Relations relationship manager.",t.jsx("br",{}),"Thank you."]}),D=t.jsx("span",{children:"Current registration status:"}),U=t.jsx("br",{}),F=t.jsx("br",{}),e[17]=$,e[18]=D,e[19]=U,e[20]=F):($=e[17],D=e[18],U=e[19],F=e[20]);let j;e[21]!==y||e[22]!==l||e[23]!==o||e[24]!==s||e[25]!==r||e[26]!==B?(j=o?t.jsxs("ul",{children:[t.jsx("li",{children:t.jsx("span",{children:"You are logged in as a Compendium Administrator"})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx(W,{to:"/survey/admin/surveys",children:"here"})," to access the survey management page."]})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx(W,{to:"/survey/admin/users",children:"here"})," to access the user management page."]})}),t.jsx("li",{children:t.jsxs("span",{children:["Click ",t.jsx("a",{href:"#",onClick:Q,children:"here"})," to do the full data download."]})})]}):t.jsxs("ul",{children:[y&&!o&&!s&&l&&B(),r?t.jsx("li",{children:t.jsx("span",{children:"You are logged in"})}):t.jsx("li",{children:t.jsx("span",{children:"You are not logged in"})}),r&&!s&&!l&&t.jsx("li",{children:t.jsx("span",{children:"Your access to the survey has not yet been approved"})}),r&&!s&&!l&&t.jsx("li",{children:t.jsx("span",{children:"Once you have been approved, you will immediately be directed to the relevant survey upon visiting this page"})}),r&&s&&t.jsx("li",{children:t.jsx("span",{children:"You have read-only access to the following surveys:"})})]}),e[21]=y,e[22]=l,e[23]=o,e[24]=s,e[25]=r,e[26]=B,e[27]=j):j=e[27];let v;e[28]!==s||e[29]!==r?(v=r&&s&&t.jsx(oe,{}),e[28]=s,e[29]=r,e[30]=v):v=e[30];let g;e[31]!==j||e[32]!==v?(g=t.jsx(X,{className:"py-5 grey-container",children:t.jsx(Z,{children:t.jsxs("div",{className:"center-text",children:[k,t.jsxs("div",{className:"wordwrap pt-4",style:T,children:[$,D,U,F,j,v]})]})})}),e[31]=j,e[32]=v,e[33]=g):g=e[33];let G;return e[34]!==g||e[35]!==p?(G=t.jsxs(t.Fragment,{children:[p,g]}),e[34]=g,e[35]=p,e[36]=G):G=e[36],G}function le(e){console.error("Error fetching data:",e),alert("An error occurred while creating the data download Excel file.")}function ae(e){if(!e.ok)throw new Error("Network response was not ok");return e.json()}export{fe as default};
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment