Ero sivun ”Eurykleia” versioiden välillä

Opasnet Suomista
Siirry navigaatioon Siirry hakuun
(→‎Laskenta: YKE, YKH ja YKM lisätty)
 
(2 välissä olevaa versiota samalta käyttäjältä ei näytetä)
Rivi 16: Rivi 16:


Alla on peruskoodi tietojen hakemiseen Yhteistyötiloista ja joidenkin perusraporttien tuottaminen. Idea sinänsä on erittäin joustava, ja R:llä on helppo toteuttaa millaista raportointia tai seurantaa tahansa. Tiedot voidaan myös kytkeä Shinyyn, joka tehokkaana R-rajapintana auttaa tekemään monenlaisia kuvaajia talous- ja henkilöstötilanteesta.
Alla on peruskoodi tietojen hakemiseen Yhteistyötiloista ja joidenkin perusraporttien tuottaminen. Idea sinänsä on erittäin joustava, ja R:llä on helppo toteuttaa millaista raportointia tai seurantaa tahansa. Tiedot voidaan myös kytkeä Shinyyn, joka tehokkaana R-rajapintana auttaa tekemään monenlaisia kuvaajia talous- ja henkilöstötilanteesta.
==== Laske rahatilanne ====


* [http://fi.opasnet.org/fi-opwiki/index.php?title=Toiminnot:RTools&id=33FYS1EH8SXqyiVS Esimerkkiajo 27.11.2017] (kuvaaja ei välttämättä näy vyvi-verkossa)
* [http://fi.opasnet.org/fi-opwiki/index.php?title=Toiminnot:RTools&id=33FYS1EH8SXqyiVS Esimerkkiajo 27.11.2017] (kuvaaja ei välttämättä näy vyvi-verkossa)
Rivi 29: Rivi 31:
library(lubridate)
library(lubridate)


############# Generic functions
objects.latest("Op_fi5758", code_name="initiate") # Generic functions [[Eurykleia]]
 
### Fetches a table based on a topic
hae <- function(aihe) {
  aihe <- match(aihe, tables$Kuvaus)
  out <- html_table(read_html(tables$URL[aihe]))[[tables$Taulu[aihe]]]
  out$Yksikkö <- tables$Yksikkö[aihe]
  return(out)
}
 
#### Takes a string and separates start and end dates with 1 month precision
jakso <- function(
  obj, # obj is character vector with date range e.g. 1.1.2017-31.12.2017,
  al # al is 1 for beginning, 2 for end.
) {
  obj <- gsub("", "-", obj) # COnfluence has a habit of automatically changing these but some problem with gsub.
  floor_date(
    as.Date(
      unlist(lapply(strsplit(obj, "-"), function(x) x[[al]])),
      format="%d.%m.%Y"
    ),
    "month"
  )
}
 
#### Changes input from percentages to fractions
pros <- function(x) {
  if(!is.numeric(x)) x <- as.numeric(as.character(gsub("[ %]", "", x)))/100
  return(x)
}
 
#### Divides a repeating event to each month in the period
aikajako <- function(
  x, # data.frame to be divided
  divs = NULL # names of columns that are to be divided rather than copied
) {
  out <- data.frame()
  for(i in 1:nrow(x)) {
    k <- round(as.numeric(jakso(x$Aika[i],2) - jakso(x$Aika[i],1))/30.25)+1
    out <- rbind(
      out,
      cbind(
        Aika = seq(jakso(x$Aika[i],1), by = "month", length = k),
        x[i,colnames(x)!="Aika"],
        k = k
      )
    )
  }
  if(!is.null(divs)) {
    for(i in divs) {
      out[[i]] <- out[[i]]/out$k
    }
  }
  out$k <- NULL
  return(out)
}
 
### Calculates the salaries for each period
palkanlaskenta <- function(x) {
  x$Palkat <- x$Htkk * x$Palkka
  x$Sivukulut <- x$Palkat * x$HSK
  x$Yleiskustannus <- (x$Palkat + x$Sivukulut) * x$YKH
  out <- melt(
    x,
    measure.vars = c("Palkat","Sivukulut","Yleiskustannus"),
    variable.name = "Kululaji",
    value.name="Summa"
  )
  out$Summa <- -out$Summa
  return(out)
}


################### Fetch all data tables
################### Fetch all data tables


### YMAL specific data
### YMAL specific data
tables <- html_table(read_html("https://terho.thl.fi/wiki01/x/3wMtCQ"))[[1]]
tables <- html_table(read_html("https://terho.thl.fi/wiki01/x/3wMtCQ"))[[1]]
henkilot <- cbind(
henkilot <- cbind(
   hae("Henkilöiden tiedot"),  
   hae("Henkilöiden tiedot"),  
   read.csv("//cesium/jtue$/_Documents/ymalpalkat.csv")
   read.csv("//cesium/jtue$/_Documents/ymalpalkat.csv")
Rivi 123: Rivi 55:


projektit.loppu <- data.frame(
projektit.loppu <- data.frame(
   projektit[c("Yksikkö","Projekti")],
   projektit[c("Yksikko","Projekti")],
   Aika = jakso(projektit$Aika,2)
   Aika = jakso(projektit$Aika,2)
)
)
projektit.loppu <- aggregate(projektit.loppu["Aika"],by=projektit.loppu[c("Yksikkö","Projekti")],FUN=max)
projektit.loppu <- aggregate(projektit.loppu["Aika"],by=projektit.loppu[c("Yksikko","Projekti")],FUN=max)


projektit <- aikajako(projektit, c("Summa", "YKE"))
projektit <- aikajako(projektit, c("Summa", "YKE"))
Rivi 132: Rivi 64:
projektit$YKH <- pros(projektit$YKH)
projektit$YKH <- pros(projektit$YKH)
projektit$YKM <- pros(projektit$YKM)
projektit$YKM <- pros(projektit$YKM)
projektit$Voitto <- pros(projektit$Voitto)
projektit$Teho <- pros(projektit$Teho)
projektit$Omarahoitus <- pros(projektit$Omarahoitus)
projektit$Omarahoitus <- pros(projektit$Omarahoitus)
projektit$"Tehollinen työaika" <- pros(projektit$"Tehollinen työaika")


henkilot$"Osa-aika" <- pros(henkilot$"Osa-aika")
henkilot$Osuus <- pros(henkilot$Osuus) # This is not used anywhere in the code?!


palkat <- merge(
palkat <- merge(
Rivi 153: Rivi 86:
   )
   )
)
)
   
 
### Check for working hours
### Check for working hours


temp <- aggregate(palkat["Htkk"], by=palkat[c("Yksikkö","Nimi","Aika")],FUN=sum)
temp <- aggregate(palkat["Htkk"], by=palkat[c("Yksikko","Nimi","Aika")],FUN=sum)


ggplot(temp, aes(x=Aika, y=Htkk, colour=Nimi))+geom_line(size=2)+
ggplot(temp, aes(x=Aika, y=Htkk, colour=Nimi))+geom_line(size=2)+
Rivi 162: Rivi 95:


temp$Aika <- floor_date(temp$Aika, "year")
temp$Aika <- floor_date(temp$Aika, "year")
oprint(aggregate(temp["Htkk"],by=temp[c("Yksikkö","Aika","Nimi")],FUN=mean))
oprint(aggregate(temp["Htkk"],by=temp[c("Yksikko","Aika","Nimi")],FUN=mean))


### Calculate salary and other events
### Calculate salary and other events
Rivi 168: Rivi 101:
palkat <- palkanlaskenta(palkat)
palkat <- palkanlaskenta(palkat)


colu <- c("Yksikkö","Projekti","Aika","Kululaji","Summa")
colu <- c("Yksikko","Projekti","Aika","Kululaji","Summa")


tap <- rbind(
tap <- rbind(
Rivi 180: Rivi 113:
   )[colu]
   )[colu]
)
)
voitto <- merge(
  tap[tap$Kululaji!="Myöntö",],
  projektit[colnames(projektit)!="Summa"]
)
tap <- rbind(
  tap,
  cbind(
    voitto[colu[-c(4,5)]],
    Kululaji = "Voittotuloutus",
    Summa = voitto$Summa * voitto$Voitto
  )
)
tap$Summa[is.na(tap$Summa)] <- 0 # This may hide problems with matching
tap$Summa[is.na(tap$Summa)] <- 0 # This may hide problems with matching
tap <- aggregate(tap["Summa"], by = tap[colu[-5]], FUN=sum)
tap <- aggregate(tap["Summa"], by = tap[colu[-5]], FUN=sum)
Rivi 187: Rivi 134:
######## Calculate the final accounts (tilinpäätös)
######## Calculate the final accounts (tilinpäätös)


tappio <- aggregate(tap["Summa"], by=tap[c("Yksikkö","Projekti")], FUN=sum)
tappio <- aggregate(tap["Summa"], by=tap[c("Yksikko","Projekti")], FUN=sum)
tappio <- tappio[tappio$Summa<0 & tappio$Projekti!="tubu",]
tappio <- tappio[tappio$Summa<0 & tappio$Projekti!="tubu",]


temp <- tap[tap$Kululaji %in% c("Palkat","Sivukulut","Yleiskustannus"),]
temp <- tap[tap$Kululaji %in% c("Palkat","Sivukulut","Yleiskustannus"),]
temp <-  aggregate(temp["Summa"], by=temp[c("Yksikkö","Projekti")],FUN=sum)
temp <-  aggregate(temp["Summa"], by=temp[c("Yksikko","Projekti")],FUN=sum)
temp2 <- tap[tap$Kululaji %in% c("Palkat"),]
temp2 <- tap[tap$Kululaji %in% c("Palkat"),]
temp2 <-  aggregate(temp2$Summa, by=temp2[c("Yksikkö","Projekti")],FUN=sum)
temp2 <-  aggregate(temp2$Summa, by=temp2[c("Yksikko","Projekti")],FUN=sum)


tappio <- merge(
tappio <- merge(
   projektit.loppu,
   projektit.loppu,
   merge(
   merge(
     merge(tappio, temp, by=c("Yksikkö","Projekti")),
     merge(tappio, temp, by=c("Yksikko","Projekti")),
     temp2
     temp2
   )
   )
Rivi 210: Rivi 157:
tappio <- merge(
tappio <- merge(
   tappio,
   tappio,
   aggregate(resurssit["Htkk"],by=resurssit[c("Yksikkö","Projekti")],FUN=sum)
   aggregate(resurssit["Htkk"],by=resurssit[c("Yksikko","Projekti")],FUN=sum)
)
)
# Overspent pm = Overspent salary / (total salary/total pm)
# Overspent pm = Overspent salary / (total salary/total pm)
tappio$Ylitys.htkk <- abs(tappio$Palkka / (tappio$x/tappio$Htkk))
tappio$Ylitys.htkk <- abs(tappio$Palkka / (tappio$x/tappio$Htkk))
cat("Projekteja ylitetty tällä htkk-määrällä\n")
cat("Projekteja ylitetty tällä htkk-määrällä\n")
tappio[c("Yksikkö","Projekti","Ylitys.htkk")]
tappio[c("Yksikko","Projekti","Ylitys.htkk")]


tap <- rbind(
tap <- rbind(
   tap, #Previous events
   tap, #Previous events
   cbind( # Project deficits corrected from tubu
   cbind( # Project deficits corrected from tubu
     tappio[c("Yksikkö","Projekti","Aika","Summa")],
     tappio[c("Yksikko","Projekti","Aika","Summa")],
     Kululaji = "Tubusiirto"
     Kululaji = "Tubusiirto"
   ),
   ),
Rivi 228: Rivi 175:
       cbind(
       cbind(
         Projekti = "tubu",
         Projekti = "tubu",
         tappio[c("Yksikkö","Aika","Palkka")],
         tappio[c("Yksikko","Aika","Palkka")],
         Htkk = 1
         Htkk = 1
       )
       )
Rivi 249: Rivi 196:


d <- rbind(
d <- rbind(
   aggregate(tap["Summa"], by = tap[c("Aika","Projekti","Yksikkö")], FUN="sum"),
   aggregate(tap["Summa"], by = tap[c("Aika","Projekti","Yksikko")], FUN="sum"),
   cbind(
   cbind(
     aggregate(tap["Summa"], by = tap[c("Aika","Yksikkö")], FUN="sum"),
     aggregate(tap["Summa"], by = tap[c("Aika","Yksikko")], FUN="sum"),
     Projekti = "Total"
     Projekti = "Total"
   )
   )
Rivi 261: Rivi 208:
)  
)  


d <- d[!d$Projekti %in% c("Batman","DWDSOME","Goherr","IM-Vesi","KaivosVV","Mineview","Tekaisu2017"),]
ggplot(d, aes(x=Aika, y=Csum, colour=Projekti))+geom_line(size=2)+
ggplot(d, aes(x=Aika, y=Csum, colour=Projekti))+geom_line(size=2)+
   labs(title="YMALin rahatilanteen kehitys projekteittain")
   labs(title="YMALin rahatilanteen kehitys projekteittain")
</rcode>
==== Initiate functions ====
<rcode name="initiate" label="Initiate functions (for developers only)">
# This is code Op_fi5758/initiate on page [[Eurykleia]]
library(OpasnetUtils)
############# Generic functions
### Fetches a table based on a topic
hae <- function(aihe) {
  aihe <- match(aihe, tables$Kuvaus)
  out <- html_table(read_html(tables$URL[aihe]))[[tables$Taulu[aihe]]]
  out$Yksikko <- tables$Yksikko[aihe]
  return(out)
}
#### Takes a string and separates start and end dates with 1 month precision
jakso <- function(
  obj, # obj is character vector with date range e.g. 1.1.2017-31.12.2017,
  al # al is 1 for beginning, 2 for end.
) {
  obj <- gsub("–", "-", obj) # COnfluence has a habit of automatically changing these but some problem with gsub.
  floor_date(
    as.Date(
      unlist(lapply(strsplit(obj, "-"), function(x) x[[al]])),
      format="%d.%m.%Y"
    ),
    "month"
  )
}
#### Changes input from percentages to fractions
pros <- function(x) {
  if(!is.numeric(x)) x <- as.numeric(as.character(gsub("[ %]", "", x)))/100
  return(x)
}
#### Divides a repeating event to each month in the period
aikajako <- function(
  x, # data.frame to be divided
  divs = NULL # names of columns that are to be divided rather than copied
) {
  out <- data.frame()
  for(i in 1:nrow(x)) {
    k <- round(as.numeric(jakso(x$Aika[i],2) - jakso(x$Aika[i],1))/30.25)+1
    out <- rbind(
      out,
      data.frame(
        Aika = seq(jakso(x$Aika[i],1), by = "month", length = k),
        x[i,colnames(x)!="Aika"],
        k = k,
        row.names=NULL
      )
    )
  }
  if(!is.null(divs)) {
    for(i in divs) {
      out[[i]] <- out[[i]]/out$k
    }
  }
  out$k <- NULL
  return(out)
}
### Calculates the salaries for each period
palkanlaskenta <- function(x) {
  x$Palkat <- x$Htkk * x$Palkka
  x$Sivukulut <- x$Palkat * x$HSK
  x$Yleiskustannus <- (x$Palkat + x$Sivukulut) * x$YKH
  out <- melt(
    x,
    measure.vars = c("Palkat","Sivukulut","Yleiskustannus"),
    variable.name = "Kululaji",
    value.name="Summa"
  )
  out$Summa <- -out$Summa
  return(out)
}
rm(wiki_username)
objects.store(list=ls())
cat("Objects", ls(), "stored.\n")
</rcode>
</rcode>



Nykyinen versio 9. marraskuuta 2018 kello 14.55




Eurykleia on suunnitelma THL:n talous- ja henkilöstönhallintaohjelmaksi.

Kysymys

Millainen talous- ja henkilöstönsuunniteelu- ja -hallintaohjelma THL:een tarvittaisiin? Sen tuli erityisesti auttaa eroon irrallisista Exceleistä, joita jokainen joutuu ylläpitämään oman projektinhallintansa tueksi.

Vastaus

Yksi mahdollisuus on hyödyntää THL:n omia valmiita järjestelmiä kuten Terhoa (Confluence-wiki) ja R-tilasto-ohjelma, jota kymmenet ihmiset talossa osaavat käyttää tehokkaasti. Tarkempi ehdotus löytyy Yhteistyötiloista sivulta Ehdotus Eurykleian toteuttamiseksi Terhossa ja sen toteutusesimerkki sivulta Esimerkki Terho-toteutuksesta.

Perustelut

Laskenta

Alla on peruskoodi tietojen hakemiseen Yhteistyötiloista ja joidenkin perusraporttien tuottaminen. Idea sinänsä on erittäin joustava, ja R:llä on helppo toteuttaa millaista raportointia tai seurantaa tahansa. Tiedot voidaan myös kytkeä Shinyyn, joka tehokkaana R-rajapintana auttaa tekemään monenlaisia kuvaajia talous- ja henkilöstötilanteesta.

Laske rahatilanne

+ Näytä koodi

Initiate functions

+ Näytä koodi

Kommentteja vaatimusmäärittelyyn

Tunnisteet viittaavat Eurykleian vaatimusmäärittelyn versioon 8.12.2017 toiminnallisten vaatimusten osalta [1].

  • Vaatimukset YL01-YL27, VU01-VU16, TA01-TA09 yleensä ottaen muutkin ovat sellaisia, jotka on mahdollista rakentaa ja muokata halutulla tavalla, koska Terho-järjestelmä on tietorakenteeltaan niin joustava. Tätä ratkaisua on kuvattu Yhteistyötiloissa sivuilla Ehdotus Eurykleian toteuttamiseksi Terhossa sekä Esimerkki Terho-toteutuksesta.
  • YL12: suoritemäärää voi seurata, jos tieto syötetään määrinä ja yksikkökustannuksina eikä suoraan euroina. Toteutus vaatii oman taulukon muttei ole vaikea.
  • VU03, VU05: Budjettijako voidaan toteuttaa tapahtumina, joiden kustannuslaji on esim. "Budjettimyöntö". Nämä tapahtumat voidaan hallinnoida keskitetysti yhdestä tai hajautetusti useammasta taulusta, koska laskennallisesti kaikki taulut ovat yhtä ja samaa ja tiedot siitä periytyvät kaikkiin organisaation osiin kokonaislaskennan kautta. Osastolle jaettu budjettimyöntö on osastolle negatiivista, kun se jaetaan edelleen yksiköihin.
  • VV01-VV03: Jos tarvitaan useampia vaihtoehtoisia budjetteja, Tätä varten tarvitaan oma sarake johon määritellään skenaarion (version) nimi. Jos nimeä ei mainita tai sarake puuttuu, kyseinen tapahtuma toteutuu kaikissa skenaarioissa.
  • PR02: Budjettien täsmäämiseen on käytössä tasaustyökalu, joka kertoo paljonko kutakin yhtä kustannuslajia pitäisi tarkasteltavalla tasolla ja aikaikkunassa lisätä, jotta budjetti menisi nollille. Näin pystytään yhdellä silmäyksellä näkemään, paljonko on yli- tai alijäämää odotettavissa ja millä tavalla se saataisiin tasattua eri kustannuslajien menoja muuttamalla. Järjestelmä on joustava, koska se voidaan laskea mille tahansa tarkasteluun otetulle tilanteelle.
  • PR09: Perustuu Terhon omaan versiohistorian toiminnallisuuteen.
  • TA10-TA13, HE21: Rivitason tapahtumat ovat suojatulla serverillä, ja sen tekninen toteutus on tällä hetkellä epäselvä. Ne joilla on pääsy suoraan tälle serverille on myös mahdollisuus tarkastella rivitason tapahtumia. Tänne voidaan rakentaan halutut toiminnallisuudet. Yleisessä käyttöliittymässä voitaisiin tuottaa linkki, jolla haetaan halutut rivitason tiedot (ja joka toimii vain jos henkilöllä on oikeudet). Voi olla, että tämä olisi helpompaa toteuttaa suoraan Kiekussa.
  • HE01-HE04: Tarkoitus on Eurykleiassa käyttää vain henkilön nimeä tunnisteena, ja palkka- ym. tiedot nousevat laskentaan suojatusta tiedostosta ja summataan, jolloin henkilöiden palkat eivät järjestelmässä sellaisenaan näy. Sen sijaan HE15:n mukaiset tiedot eli esim. työsuhteiden kestot ja sijoitukset eri projekteille näkyvät kaikille (ellei yksikkö suojaa omaa budjetointisivuaan).
  • HE13: Vaatimusmäärittelyssä jää epäselväksi, millaista luokittelua tässä ajatellaan ja pitääkö olla viisi erillistä kenttää jossa on etukäteen määrätyt vaihtoehdot. Ehdotan sen sijaan, että on yksi sarake Luokittelut, johon voi kirjoittaa useita vapaamuotoisia luokitteluja esim. pilkulla erotettuna.
  • KA01-KA06: Järjestelmä rakennetaan kaksitasoiseksi: suunnittelutiedot, menot, tulot ja henkilöiden nimet ovat lähtökohtaisesti kaikkien nähtävissä. Palkkatiedot, hetut, vaativuusluokat ja suoriutumispisteet sen sijaan eivät. Tällä hetkellä on epäselvää, onko noihin salaisiin tietoihin lainkaan tarpeellista päästä tämän järjestelmän kautta, vai katsellaanko ja hallinnoidaanko niitä suoraan siellä missä ne ovat (eli HOT:ssa?).
  • HA01-HA13 sekä TI01-TI10: hankintatiedot ja tilatiedot tarvitsevat kokonaan omat taulunsa ja laskentakoodinsa. Tämä ei ole sinänsä ongelmallista, mutta ei liene myöskään välttämätöntä toteuttaa niitä ensi vaiheessa (ja kakkoskategoriaan ne on merkittykin).
  • LI01-LI27: Rajapintaliittymät ovat lähinnä kiinni niistä järjestelmistä, joista tietoa ollaan tuomassa. Tässä suunnitelmassa oletetaan, että tietoja tuodaan eräajoina eikä reaaliaikaiseen rajapintaan pyritä. Monien tietojen osalta riittää, että tiedot tuodaan tähän järjestelmään kerran viikossa. Yksinkertaisin versio on sellainen, että joku riittävillä oikeuksilla varustettu henkilö tekee haun esim. Kiekuun ja tallentaa rivitason tiedot suojatulle serverille CSV-tiedostona, jota Eurykleia puolestaan ne lukee. Hienompiakin tapoja voi kehitellä, mutta rutiinitoimenpiteenä tässä ei pitäisi mennä paria minuuttia kauempaa ja siksi kovin massiivisia järjestelmiä tuon pienen työn säästämiseksi ei kannata rakentaa.
  • LI07: Tarvittava kääntötaulukko voidaan ylläpitää Terhossa.

Katso myös